fix(web): unify channel icon fallbacks (#397)

This commit is contained in:
Chrys
2026-04-22 22:14:41 +08:00
committed by GitHub
parent 925fdee478
commit e1dd6e15a9
8 changed files with 66 additions and 11 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

-5
View File
@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-110 -110 740 740" fill="none">
<path fill="#000" d="M13.7 11.9v496.2h35.7V520H0V0h49.4v11.9H13.7Z"/>
<path fill="#000" d="M166.3 169.2v25.1h.7c6.7-9.6 14.8-17 24.2-22.2 9.4-5.3 20.3-7.9 32.5-7.9 11.7 0 22.4 2.3 32.1 6.8 9.7 4.5 17 12.6 22.1 24 5.5-8.1 13-15.3 22.4-21.5 9.4-6.2 20.6-9.3 33.5-9.3 9.8 0 18.9 1.2 27.3 3.6 8.4 2.4 15.5 6.2 21.5 11.5 6 5.3 10.6 12.1 14 20.6 3.3 8.5 5 18.7 5 30.7v124.1h-50.9V249.6c0-6.2-.2-12.1-.7-17.6-.5-5.5-1.8-10.3-3.9-14.3-2.2-4.1-5.3-7.3-9.5-9.7-4.2-2.4-9.9-3.6-17-3.6-7.2 0-13 1.4-17.4 4.1-4.4 2.8-7.9 6.3-10.4 10.8-2.5 4.4-4.2 9.4-5 15.1-.8 5.6-1.3 11.3-1.3 17v103.3h-50.9v-104c0-5.5-.1-10.9-.4-16.3-.2-5.4-1.3-10.3-3.1-14.9-1.8-4.5-4.8-8.2-9-10.9-4.2-2.7-10.3-4.1-18.5-4.1-2.4 0-5.6.5-9.5 1.6-3.9 1.1-7.8 3.1-11.5 6.1-3.7 3-6.9 7.3-9.5 12.9-2.6 5.6-3.9 13-3.9 22.1v107.6h-50.9V169.2h50.6Z"/>
<path fill="#000" d="M506.3 508.1V11.9h-35.7V0H520v520h-49.4v-11.9h35.7Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

+15 -3
View File
@@ -7,6 +7,8 @@
/>
<span
v-else
class="inline-flex items-center justify-center font-medium leading-none"
:style="fallbackStyle"
v-bind="$attrs"
>{{ fallback }}</span>
</template>
@@ -24,7 +26,9 @@ import {
Wechatoa,
Wecom,
Matrix,
Misskey,
} from '@memohai/icon'
import { channelIconFallbackText } from '@/utils/channel-icon-fallback'
const channelIcons: Record<string, Component> = {
qq: Qq,
@@ -37,7 +41,7 @@ const channelIcons: Record<string, Component> = {
wechatoa: Wechatoa,
wecom: Wecom,
matrix: Matrix,
// misskey: Misskey,
misskey: Misskey,
dingtalk: Dingtalk,
}
@@ -50,11 +54,19 @@ const props = withDefaults(defineProps<{
defineOptions({ inheritAttrs: false })
const normalizedChannel = computed(() =>
props.channel.trim().toLowerCase(),
)
const iconComponent = computed<Component | undefined>(() =>
channelIcons[props.channel],
channelIcons[normalizedChannel.value],
)
const fallback = computed(() =>
props.channel.slice(0, 2).toUpperCase(),
channelIconFallbackText(props.channel),
)
const fallbackStyle = computed(() => ({
fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size,
}))
</script>
@@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest'
import { channelIconFallbackText } from './channel-icon-fallback'
describe('channelIconFallbackText', () => {
it('returns empty text for empty channel keys', () => {
expect(channelIconFallbackText('')).toBe('')
expect(channelIconFallbackText(' ')).toBe('')
})
it('uses explicit built-in fallbacks for non-brand channels', () => {
expect(channelIconFallbackText('local')).toBe('LC')
expect(channelIconFallbackText('cli')).toBe('CLI')
expect(channelIconFallbackText('web')).toBe('Web')
})
it('normalizes casing and whitespace', () => {
expect(channelIconFallbackText(' Discord ')).toBe('DI')
})
it('creates initials for multi-part unknown channels', () => {
expect(channelIconFallbackText('custom-bridge')).toBe('CB')
expect(channelIconFallbackText('foo_bar')).toBe('FB')
})
it('uses the first two alphanumeric characters for simple unknown channels', () => {
expect(channelIconFallbackText('misskey')).toBe('MI')
expect(channelIconFallbackText('qq')).toBe('QQ')
})
})
@@ -0,0 +1,20 @@
const explicitChannelFallbacks: Record<string, string> = {
cli: 'CLI',
local: 'LC',
web: 'Web',
}
export function channelIconFallbackText(channel: string): string {
const normalized = channel.trim().toLowerCase()
if (!normalized) return ''
const explicit = explicitChannelFallbacks[normalized]
if (explicit) return explicit
const parts = normalized.match(/[a-z0-9]+/gi) ?? []
if (parts.length > 1) {
return parts.slice(0, 2).map((part) => part[0]?.toUpperCase() ?? '').join('')
}
return normalized.replace(/[^a-z0-9]/gi, '').slice(0, 2).toUpperCase()
}
+1 -3
View File
@@ -23,9 +23,7 @@ var embeddedStaticRoutes = map[string]struct {
assetPath string
contentType string
}{
"/logo.png": {assetPath: "logo.png", contentType: "image/png"},
"/channels/telegram.webp": {assetPath: "channels/telegram.webp", contentType: "image/webp"},
"/channels/feishu.png": {assetPath: "channels/feishu.png", contentType: "image/png"},
"/logo.png": {assetPath: "logo.png", contentType: "image/png"},
}
func NewEmbeddedWebHandler(log *slog.Logger) (*EmbeddedWebHandler, error) {
+1
View File
@@ -51,6 +51,7 @@ export { default as Microsoft } from './icons/Microsoft.vue'
export { default as MicrosoftColor } from './icons/MicrosoftColor.vue'
export { default as Minimax } from './icons/Minimax.vue'
export { default as MinimaxColor } from './icons/MinimaxColor.vue'
export { default as Misskey } from './icons/Misskey.vue'
export { default as Mistral } from './icons/Mistral.vue'
export { default as MistralColor } from './icons/MistralColor.vue'
export { default as Moonshot } from './icons/Moonshot.vue'