diff --git a/packages/examples/packages/images/snap.manifest.json b/packages/examples/packages/images/snap.manifest.json index d071ce7856..f0c0c2e667 100644 --- a/packages/examples/packages/images/snap.manifest.json +++ b/packages/examples/packages/images/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "UKBWUM+VMGi3YPuP2zhT3fpfB5MgN7muuU2MBaW5964=", + "shasum": "6hmcWrwBG05b5UlKIzJppBhfqqi+C/YxFSQgJrFjh64=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/examples/packages/images/src/images/icon.png b/packages/examples/packages/images/src/images/icon.png new file mode 100644 index 0000000000..080a3b8477 Binary files /dev/null and b/packages/examples/packages/images/src/images/icon.png differ diff --git a/packages/examples/packages/images/src/images/icon.svg b/packages/examples/packages/images/src/images/icon.svg new file mode 100644 index 0000000000..5dc14b4dc1 --- /dev/null +++ b/packages/examples/packages/images/src/images/icon.svg @@ -0,0 +1,5 @@ + + + diff --git a/packages/examples/packages/images/src/index.test.ts b/packages/examples/packages/images/src/index.test.ts index eae8ddbd78..f398bcf39a 100644 --- a/packages/examples/packages/images/src/index.test.ts +++ b/packages/examples/packages/images/src/index.test.ts @@ -26,7 +26,7 @@ describe('onRpcRequest', () => { describe('getQrCode', () => { it('generates a QR code for the given data', async () => { - const { request, close } = await installSnap(); + const { request } = await installSnap(); const response = request({ method: 'getQrCode', @@ -46,16 +46,14 @@ describe('onRpcRequest', () => { await ui.ok(); expect(await response).toRespondWith(null); - - await close(); }); }); describe('getCat', () => { - // This test is flaky so we disable it for now + // This test is flaky, so we disable it for now. // eslint-disable-next-line jest/no-disabled-tests it.skip('shows a cat', async () => { - const { request, close } = await installSnap(); + const { request } = await installSnap(); const response = request({ method: 'getCat', @@ -84,8 +82,66 @@ describe('onRpcRequest', () => { await ui.ok(); expect(await response).toRespondWith(null); + }); + }); + + describe('getSvgIcon', () => { + it('shows an SVG icon', async () => { + const { request } = await installSnap(); + + const response = request({ + method: 'getSvgIcon', + }); + + const ui = await response.getInterface(); + // eslint-disable-next-line jest/prefer-strict-equal + expect(ui.content).toEqual({ + type: 'panel', + children: [ + { + type: 'text', + value: 'Here is an SVG icon:', + }, + { + type: 'image', + value: expect.stringContaining(' { + it('shows a PNG icon', async () => { + const { request } = await installSnap(); - await close(); + const response = request({ + method: 'getPngIcon', + }); + + const ui = await response.getInterface(); + // eslint-disable-next-line jest/prefer-strict-equal + expect(ui.content).toEqual({ + type: 'panel', + children: [ + { + type: 'text', + value: 'Here is a PNG icon:', + }, + { + type: 'image', + value: expect.stringContaining('data:image/png;base64'), + }, + ], + }); + + await ui.ok(); + + expect(await response).toRespondWith(null); }); }); }); diff --git a/packages/examples/packages/images/src/index.ts b/packages/examples/packages/images/src/index.ts index b81544d0ea..42e71a7f17 100644 --- a/packages/examples/packages/images/src/index.ts +++ b/packages/examples/packages/images/src/index.ts @@ -9,6 +9,9 @@ import { } from '@metamask/snaps-sdk'; import { renderSVG } from 'uqr'; +import pngIcon from './images/icon.png'; +import svgIcon from './images/icon.svg'; + /** * The parameters for the `getQrCode` method. * @@ -74,6 +77,32 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => { }); } + case 'getSvgIcon': { + return await snap.request({ + method: 'snap_dialog', + params: { + type: DialogType.Alert, + + // `.svg` files are imported as strings, so they can be used directly + // with the `image` component. + content: panel([text('Here is an SVG icon:'), image(svgIcon)]), + }, + }); + } + + case 'getPngIcon': { + return await snap.request({ + method: 'snap_dialog', + params: { + type: DialogType.Alert, + + // `.png` files are imported as SVGs containing an `` tag, + // so they can be used directly with the `image` component. + content: panel([text('Here is a PNG icon:'), image(pngIcon)]), + }, + }); + } + default: throw new MethodNotFoundError({ method: request.method }); } diff --git a/packages/snaps-cli/src/config.ts b/packages/snaps-cli/src/config.ts index 6e3a0da03b..2704c3064a 100644 --- a/packages/snaps-cli/src/config.ts +++ b/packages/snaps-cli/src/config.ts @@ -13,7 +13,6 @@ import { dim } from 'chalk'; import { readFile } from 'fs/promises'; import Module from 'module'; import { basename, dirname, resolve } from 'path'; -import type { Infer } from 'superstruct'; import { array, boolean, @@ -30,6 +29,7 @@ import { unknown, empty, } from 'superstruct'; +import type { Infer } from 'superstruct'; import type { Configuration as WebpackConfiguration } from 'webpack'; import { TranspilationModes } from './builders'; @@ -415,6 +415,27 @@ export type SnapWebpackConfig = { zlib?: boolean; }; + /** + * Optional features to enable in the CLI. + * + * @example + * { + * features: { + * images: true, + * } + * } + */ + features?: { + /** + * Whether to enable support for images. If `true`, the Webpack + * configuration will be modified to support images. If `false`, the + * Webpack configuration will not be modified. + * + * @default true + */ + images?: boolean; + }; + /** * A function to customize the Webpack configuration used to build the snap. * This function will be called with the default Webpack configuration, and @@ -618,6 +639,13 @@ export const SnapsWebpackConfigStruct = object({ false, ), + features: defaulted( + object({ + images: defaulted(boolean(), true), + }), + {}, + ), + customizeWebpackConfig: optional( SnapsWebpackCustomizeWebpackConfigFunctionStruct, ), diff --git a/packages/snaps-cli/src/index.ts b/packages/snaps-cli/src/index.ts index 11339ecf49..fe0fa4f168 100644 --- a/packages/snaps-cli/src/index.ts +++ b/packages/snaps-cli/src/index.ts @@ -15,4 +15,4 @@ export type { } from './webpack'; // Re-exported from `snaps-cli` for convenience. -export { merge } from 'webpack-merge'; +export { merge, mergeWithCustomize, mergeWithRules } from 'webpack-merge'; diff --git a/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap b/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap index d2bd52e2bb..c51deb72ba 100644 --- a/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap +++ b/packages/snaps-cli/src/webpack/__snapshots__/config.test.ts.snap @@ -36,7 +36,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -178,7 +196,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -320,7 +356,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -462,7 +516,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -595,7 +667,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, { "test": /\\\\\\.wasm\\$/u, @@ -745,7 +835,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -887,7 +995,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1029,7 +1155,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1179,7 +1323,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1293,6 +1455,151 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t } `; +exports[`getDefaultConfiguration returns the default Webpack configuration for the given CLI config 10`] = ` +{ + "devtool": false, + "entry": "/foo/bar/src/index.js", + "infrastructureLogging": { + "level": "none", + }, + "mode": "production", + "module": { + "rules": [ + { + "exclude": /node_modules/u, + "test": /\\\\\\.\\(js\\|mjs\\|cjs\\|ts\\)\\$/u, + "use": { + "loader": "/foo/bar/node_modules/swc-loader/index.js", + "options": { + "env": { + "targets": "chrome >= 90, firefox >= 91", + }, + "jsc": { + "parser": { + "syntax": "typescript", + }, + }, + "module": { + "type": "es6", + }, + "sourceMaps": false, + "sync": false, + }, + }, + }, + { + "resolve": { + "fullySpecified": false, + }, + "test": /\\\\\\.m\\?js\\$/u, + }, + false, + false, + false, + false, + ], + }, + "optimization": { + "minimize": false, + "minimizer": [ + TerserPlugin { + "options": { + "exclude": undefined, + "extractComments": true, + "include": undefined, + "minimizer": { + "implementation": [Function], + "options": {}, + }, + "parallel": true, + "test": /\\\\\\.\\[cm\\]\\?js\\(\\\\\\?\\.\\*\\)\\?\\$/i, + }, + }, + ], + }, + "output": { + "chunkFormat": "commonjs", + "clean": false, + "filename": "bundle.js", + "library": { + "type": "commonjs", + }, + "path": "/foo/bar/dist", + "publicPath": "/", + }, + "performance": { + "hints": false, + }, + "plugins": [ + SnapsWebpackPlugin { + "options": { + "eval": true, + "manifestPath": "/foo/bar/snap.manifest.json", + "writeManifest": true, + }, + }, + SnapsStatsPlugin { + "options": { + "verbose": false, + }, + }, + DefinePlugin { + "definitions": { + "process.env.DEBUG": ""false"", + "process.env.NODE_DEBUG": ""false"", + "process.env.NODE_ENV": ""production"", + }, + }, + ProgressPlugin { + "dependenciesCount": 10000, + "handler": [Function], + "modulesCount": 5000, + "percentBy": undefined, + "profile": false, + "showActiveModules": false, + "showDependencies": true, + "showEntries": true, + "showModules": true, + }, + SnapsBundleWarningsPlugin { + "options": { + "buffer": true, + "builtInResolver": SnapsBuiltInResolver { + "options": { + "ignore": [], + }, + "unresolvedModules": Set {}, + }, + "builtIns": true, + }, + }, + ], + "resolve": { + "extensions": [ + ".js", + ".mjs", + ".cjs", + ".ts", + ], + "fallback": { + "buffer": false, + "fs": false, + "path": false, + }, + "plugins": [ + SnapsBuiltInResolver { + "options": { + "ignore": [], + }, + "unresolvedModules": Set {}, + }, + ], + }, + "stats": "none", + "target": "browserslist:/foo/bar/.browserslistrc", +} +`; + exports[`getDefaultConfiguration returns the default Webpack configuration for the given CLI config and options 1`] = ` { "devtool": false, @@ -1329,7 +1636,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1480,7 +1805,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1622,7 +1965,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1764,7 +2125,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -1915,7 +2294,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -2070,7 +2467,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -2206,7 +2621,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], @@ -2342,7 +2775,25 @@ exports[`getDefaultConfiguration returns the default Webpack configuration for t "resolve": { "fullySpecified": false, }, - "test": /\\\\\\.m\\?js/u, + "test": /\\\\\\.m\\?js\\$/u, + }, + { + "test": /\\\\\\.svg\\$/u, + "type": "asset/source", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.png\\$/u, + "type": "asset/inline", + }, + { + "generator": { + "dataUrl": [Function], + }, + "test": /\\\\\\.jpe\\?g\\$/u, + "type": "asset/inline", }, false, ], diff --git a/packages/snaps-cli/src/webpack/config.test.ts b/packages/snaps-cli/src/webpack/config.test.ts index 0404de4255..afc7e27c7f 100644 --- a/packages/snaps-cli/src/webpack/config.test.ts +++ b/packages/snaps-cli/src/webpack/config.test.ts @@ -150,6 +150,19 @@ describe('getDefaultConfiguration', () => { buffer: true, }, }), + getMockConfig('webpack', { + input: 'src/index.js', + output: { + path: 'dist', + minimize: false, + }, + manifest: { + path: 'snap.manifest.json', + }, + features: { + images: false, + }, + }), ])( 'returns the default Webpack configuration for the given CLI config', async (config) => { diff --git a/packages/snaps-cli/src/webpack/config.ts b/packages/snaps-cli/src/webpack/config.ts index 4a8553da98..cc1c01d3a8 100644 --- a/packages/snaps-cli/src/webpack/config.ts +++ b/packages/snaps-cli/src/webpack/config.ts @@ -19,6 +19,7 @@ import { getDevTool, getEnvironmentVariables, getFallbacks, + getImageSVG, getProgressHandler, } from './utils'; @@ -199,12 +200,43 @@ export async function getDefaultConfiguration( * @see https://webpack.js.org/configuration/module/#resolvefullyspecified */ { - test: /\.m?js/u, + test: /\.m?js$/u, resolve: { fullySpecified: false, }, }, + /** + * This allows importing `.svg` files as a string. + */ + config.features.images && { + test: /\.svg$/u, + // `asset/source` returns the source as a UTF-8 string. + type: 'asset/source', + }, + + /** + * This allows importing `.png` files as a data URL. + */ + config.features.images && { + test: /\.png$/u, + type: 'asset/inline', + generator: { + dataUrl: getImageSVG.bind(null, 'image/png'), + }, + }, + + /** + * This allows importing `.jpe?g` files as a data URL. + */ + config.features.images && { + test: /\.jpe?g$/u, + type: 'asset/inline', + generator: { + dataUrl: getImageSVG.bind(null, 'image/jpeg'), + }, + }, + config.experimental.wasm && { test: /\.wasm$/u, use: getFunctionLoader(wasm, {}), diff --git a/packages/snaps-cli/src/webpack/utils.test.ts b/packages/snaps-cli/src/webpack/utils.test.ts index 1979050e1a..26a440f697 100644 --- a/packages/snaps-cli/src/webpack/utils.test.ts +++ b/packages/snaps-cli/src/webpack/utils.test.ts @@ -13,6 +13,7 @@ import { pluralize, getEnvironmentVariables, formatText, + getImageSVG, } from './utils'; describe('getDefaultLoader', () => { @@ -229,3 +230,19 @@ describe('formatText', () => { `); }); }); + +describe('getImageSVG', () => { + it('returns an SVG string for a PNG image', () => { + const image = getImageSVG('image/png', new Uint8Array([1, 2, 3])); + expect(image).toMatchInlineSnapshot( + `""`, + ); + }); + + it('returns an SVG string for a JPEG image', () => { + const image = getImageSVG('image/jpeg', new Uint8Array([1, 2, 3])); + expect(image).toMatchInlineSnapshot( + `""`, + ); + }); +}); diff --git a/packages/snaps-cli/src/webpack/utils.ts b/packages/snaps-cli/src/webpack/utils.ts index f33699b14f..3ea8422cb7 100644 --- a/packages/snaps-cli/src/webpack/utils.ts +++ b/packages/snaps-cli/src/webpack/utils.ts @@ -1,3 +1,4 @@ +import { bytesToBase64 } from '@metamask/utils'; import { dim } from 'chalk'; import { promises as fs } from 'fs'; import { builtinModules } from 'module'; @@ -361,3 +362,15 @@ export function formatText( }) .join('\n'); } + +/** + * Get an SVG from the given bytes and mime type. + * + * @param mimeType - The mime type of the image. + * @param bytes - The image bytes. + * @returns The SVG. + */ +export function getImageSVG(mimeType: string, bytes: Uint8Array) { + const dataUrl = `data:${mimeType};base64,${bytesToBase64(bytes)}`; + return ``; +} diff --git a/packages/snaps-sdk/src/types/images.ts b/packages/snaps-sdk/src/types/images.ts new file mode 100644 index 0000000000..c36e8789cd --- /dev/null +++ b/packages/snaps-sdk/src/types/images.ts @@ -0,0 +1,15 @@ +// eslint-disable-next-line import/unambiguous +declare module '*.svg' { + const content: string; + export default content; +} + +declare module '*.png' { + const content: string; + export default content; +} + +declare module '*.jpg' { + const content: string; + export default content; +} diff --git a/packages/snaps-sdk/src/types/index.ts b/packages/snaps-sdk/src/types/index.ts index a297aeca0c..fafc24fe65 100644 --- a/packages/snaps-sdk/src/types/index.ts +++ b/packages/snaps-sdk/src/types/index.ts @@ -1,6 +1,8 @@ // This is intentionally imported, rather than re-exported. -// eslint-disable-next-line import/no-unassigned-import +/* eslint-disable import/no-unassigned-import */ import './global'; +import './images'; +/* eslint-enable import/no-unassigned-import */ export * from './caip'; export * from './handlers'; diff --git a/packages/snaps-simulator/src/components/Icon.tsx b/packages/snaps-simulator/src/components/Icon.tsx index 751f88e096..2aa7b2d5e4 100644 --- a/packages/snaps-simulator/src/components/Icon.tsx +++ b/packages/snaps-simulator/src/components/Icon.tsx @@ -225,7 +225,7 @@ export const Icon: ForwardRefExoticComponent = forwardRef( return ( {alt} { version={IMAGES_VERSION} testId="images" > - + + + ); diff --git a/packages/test-snaps/src/features/snaps/images/components/ShowImage.tsx b/packages/test-snaps/src/features/snaps/images/components/ShowImage.tsx index 85dd55c2e8..8b405a747f 100644 --- a/packages/test-snaps/src/features/snaps/images/components/ShowImage.tsx +++ b/packages/test-snaps/src/features/snaps/images/components/ShowImage.tsx @@ -6,19 +6,31 @@ import { useInvokeMutation } from '../../../../api'; import { getSnapId } from '../../../../utils'; import { IMAGES_SNAP_ID, IMAGES_SNAP_PORT } from '../constants'; -export const ShowImage: FunctionComponent = () => { +export type ShowImageProps = { + method: string; + name: string; +}; + +export const ShowImage: FunctionComponent = ({ + method, + name, +}) => { const [invokeSnap, { isLoading }] = useInvokeMutation(); const handleShowImage = () => { invokeSnap({ snapId: getSnapId(IMAGES_SNAP_ID, IMAGES_SNAP_PORT), - method: 'getCat', + method, }).catch(logError); }; return ( - ); };