feat: add sidebar brand customization
The primary sidebar brand was fixed to the built-in name, subtitle, and logo. Appearance settings now let users preview and save a custom brand name, localized Chinese and English subtitles, and a validated local PNG, JPEG, or WebP logo, with default-logo and full-reset actions. Customization remains device-local and limited to the top-left brand area; installer, update, Agent, and protocol identities remain GoodBuddy. The desktop package version is now 0.11.6. Release note: 新增“外观 → 品牌定制”,可修改左上角品牌名称、中英文副标题和本地 Logo,并支持实时预览、恢复默认及本机持久化。
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { detectSupportedImage } from './image-media-type'
|
||||
|
||||
describe('supported image detection', () => {
|
||||
it.each([
|
||||
{
|
||||
bytes: [0xff, 0xd8, 0xff],
|
||||
extension: 'jpg',
|
||||
mimeType: 'image/jpeg'
|
||||
},
|
||||
{
|
||||
bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
|
||||
extension: 'png',
|
||||
mimeType: 'image/png'
|
||||
},
|
||||
{
|
||||
bytes: [
|
||||
0x52,
|
||||
0x49,
|
||||
0x46,
|
||||
0x46,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0x57,
|
||||
0x45,
|
||||
0x42,
|
||||
0x50
|
||||
],
|
||||
extension: 'webp',
|
||||
mimeType: 'image/webp'
|
||||
}
|
||||
])('detects $mimeType bytes', ({ bytes, extension, mimeType }) => {
|
||||
expect(detectSupportedImage(Uint8Array.from(bytes))).toEqual({
|
||||
extension,
|
||||
mimeType
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects unsupported bytes', () => {
|
||||
expect(() =>
|
||||
detectSupportedImage(Uint8Array.from([1, 2, 3]))
|
||||
).toThrow('图片格式不受支持')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
export type SupportedImageFormat = {
|
||||
extension: 'jpg' | 'png' | 'webp'
|
||||
mimeType: 'image/jpeg' | 'image/png' | 'image/webp'
|
||||
}
|
||||
|
||||
function matchesAscii(
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
value: string
|
||||
): boolean {
|
||||
return [...value].every(
|
||||
(character, index) =>
|
||||
data[offset + index] === character.charCodeAt(0)
|
||||
)
|
||||
}
|
||||
|
||||
export function detectSupportedImage(
|
||||
data: Uint8Array
|
||||
): SupportedImageFormat {
|
||||
if (
|
||||
data.byteLength >= 3 &&
|
||||
data[0] === 0xff &&
|
||||
data[1] === 0xd8 &&
|
||||
data[2] === 0xff
|
||||
) {
|
||||
return { extension: 'jpg', mimeType: 'image/jpeg' }
|
||||
}
|
||||
if (
|
||||
data.byteLength >= 8 &&
|
||||
[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a].every(
|
||||
(value, index) => data[index] === value
|
||||
)
|
||||
) {
|
||||
return { extension: 'png', mimeType: 'image/png' }
|
||||
}
|
||||
if (
|
||||
data.byteLength >= 12 &&
|
||||
matchesAscii(data, 0, 'RIFF') &&
|
||||
matchesAscii(data, 8, 'WEBP')
|
||||
) {
|
||||
return { extension: 'webp', mimeType: 'image/webp' }
|
||||
}
|
||||
throw new Error('图片格式不受支持')
|
||||
}
|
||||
Reference in New Issue
Block a user