mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
feat(electron): restore bmp/avif background image support
This commit is contained in:
parent
9b58b883af
commit
0286cd3b5d
4 changed files with 45 additions and 2 deletions
|
|
@ -70,6 +70,39 @@ test('importImage copies a valid image and returns an opaque id', async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('importImage accepts avif and bmp (restored legacy formats)', async () => {
|
||||||
|
const cache = load();
|
||||||
|
const sourceDir = fsModule.realpathSync.native(
|
||||||
|
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const avif = await mkPng(sourceDir, 'bg.avif', Buffer.from('fakeavif'));
|
||||||
|
const avifResult = await cache.importImage(avif);
|
||||||
|
assert.ok(avifResult);
|
||||||
|
assert.equal(avifResult.mimeType, 'image/avif');
|
||||||
|
|
||||||
|
const bmp = await mkPng(sourceDir, 'bg.bmp', Buffer.from('fakebmp'));
|
||||||
|
const bmpResult = await cache.importImage(bmp);
|
||||||
|
assert.ok(bmpResult);
|
||||||
|
assert.equal(bmpResult.mimeType, 'image/bmp');
|
||||||
|
} finally {
|
||||||
|
await fs.rm(sourceDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('importImage rejects svg (scriptable format deliberately excluded)', async () => {
|
||||||
|
const cache = load();
|
||||||
|
const sourceDir = fsModule.realpathSync.native(
|
||||||
|
await fs.mkdtemp(path.join(os.tmpdir(), 'sp-src-')),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const svg = await mkPng(sourceDir, 'bg.svg', Buffer.from('<svg></svg>'));
|
||||||
|
assert.equal(await cache.importImage(svg), null);
|
||||||
|
} finally {
|
||||||
|
await fs.rm(sourceDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('importImage returns null for a path inside userData (no laundering)', async () => {
|
test('importImage returns null for a path inside userData (no laundering)', async () => {
|
||||||
const cache = load();
|
const cache = load();
|
||||||
const evilPath = await mkPng(userDataDir, 'planted.png');
|
const evilPath = await mkPng(userDataDir, 'planted.png');
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,17 @@ import { assertPathOutside } from './file-path-guard';
|
||||||
|
|
||||||
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
||||||
|
|
||||||
|
// SVG is deliberately excluded: it is a scriptable format (can embed
|
||||||
|
// <script>/event handlers) and inlining it as a data URL would reintroduce an
|
||||||
|
// XSS surface, so the cache only accepts raster formats.
|
||||||
const MIME_BY_EXT: Record<string, string> = {
|
const MIME_BY_EXT: Record<string, string> = {
|
||||||
png: 'image/png',
|
png: 'image/png',
|
||||||
jpg: 'image/jpeg',
|
jpg: 'image/jpeg',
|
||||||
jpeg: 'image/jpeg',
|
jpeg: 'image/jpeg',
|
||||||
gif: 'image/gif',
|
gif: 'image/gif',
|
||||||
webp: 'image/webp',
|
webp: 'image/webp',
|
||||||
|
bmp: 'image/bmp',
|
||||||
|
avif: 'image/avif',
|
||||||
};
|
};
|
||||||
|
|
||||||
const ID_RE = /^[a-f0-9]{32}$/;
|
const ID_RE = /^[a-f0-9]{32}$/;
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,12 @@ export const initLocalFileSyncAdapter = (): void => {
|
||||||
buttonLabel: 'Select',
|
buttonLabel: 'Select',
|
||||||
properties: ['openFile'],
|
properties: ['openFile'],
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp'] },
|
{
|
||||||
|
name: 'Images',
|
||||||
|
// Keep in sync with MIME_BY_EXT in image-cache.ts (svg excluded
|
||||||
|
// on purpose — scriptable format).
|
||||||
|
extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'avif'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})) as unknown as { canceled: boolean; filePaths: string[] };
|
})) as unknown as { canceled: boolean; filePaths: string[] };
|
||||||
if (canceled || !filePaths[0]) {
|
if (canceled || !filePaths[0]) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<input
|
<input
|
||||||
#fileInput
|
#fileInput
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/png,image/jpeg,image/gif,image/webp"
|
accept="image/png,image/jpeg,image/gif,image/webp,image/bmp,image/avif"
|
||||||
hidden
|
hidden
|
||||||
(change)="onFileSelected($event)"
|
(change)="onFileSelected($event)"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue