This repository was archived by the owner on Sep 21, 2026. It is now read-only.
File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ import { describe , expect , it } from 'vitest' ;
2+ import { resolveOAuth2AppLogoUrl } from './oauth2-app-logo' ;
3+
4+ describe ( 'resolveOAuth2AppLogoUrl' , ( ) => {
5+ it ( 'uses the square Appwrite icon for the first-party CLI client' , ( ) => {
6+ expect (
7+ resolveOAuth2AppLogoUrl ( {
8+ $id : 'appwrite-cli' ,
9+ logoUri : 'https://example.com/wordmark.svg'
10+ } )
11+ ) . toBe ( '/console/logos/appwrite-icon.svg' ) ;
12+ } ) ;
13+
14+ it ( 'keeps third-party logoUri values' , ( ) => {
15+ expect (
16+ resolveOAuth2AppLogoUrl ( {
17+ $id : 'other-app' ,
18+ logoUri : 'https://example.com/logo.png'
19+ } )
20+ ) . toBe ( 'https://example.com/logo.png' ) ;
21+ } ) ;
22+
23+ it ( 'returns an empty string when no logo is available' , ( ) => {
24+ expect ( resolveOAuth2AppLogoUrl ( { $id : 'other-app' , logoUri : ' ' } ) ) . toBe ( '' ) ;
25+ expect ( resolveOAuth2AppLogoUrl ( null ) ) . toBe ( '' ) ;
26+ } ) ;
27+ } ) ;
Original file line number Diff line number Diff line change 1+ import { base } from '$app/paths' ;
2+
3+ /**
4+ * First-party clients may register a wide wordmark as `logoUri`. Consent and
5+ * outcome avatars are square, so `object-fit: cover` crops those marks into
6+ * unreadable fragments (see #3159 for Appwrite CLI). Prefer the square icon.
7+ */
8+ const FIRST_PARTY_ICON_CLIENTS = new Set ( [ 'appwrite-cli' ] ) ;
9+
10+ export function resolveOAuth2AppLogoUrl (
11+ app : { $id ?: string ; logoUri ?: string } | null | undefined
12+ ) : string {
13+ if ( app ?. $id && FIRST_PARTY_ICON_CLIENTS . has ( app . $id ) ) {
14+ return `${ base } /logos/appwrite-icon.svg` ;
15+ }
16+
17+ return app ?. logoUri ?. trim ( ) ?? '' ;
18+ }
Original file line number Diff line number Diff line change 4848 type ResourcePage ,
4949 type ResourceNameMap
5050 } from ' $lib/helpers/oauth2-authorization-details' ;
51+ import { resolveOAuth2AppLogoUrl } from ' $lib/helpers/oauth2-app-logo' ;
5152 import ResourceSelector from ' ./resource-selector.svelte' ;
5253
5354 export type OAuth2Flow = ' authorization' | ' device' ;
110111 );
111112
112113 const redirectHost = $derived (hostnameOf (grant .redirectUri ));
114+ const appLogoUrl = $derived (resolveOAuth2AppLogoUrl (app ));
113115 const appInitial = $derived ((app .name || ' ?' ).charAt (0 ).toUpperCase ());
114116 const accountInitial = $derived ((accountLabel || ' ?' ).charAt (0 ).toUpperCase ());
115117
493495 <div class =" consent" >
494496 <header class =" header" >
495497 <div class =" identity" >
496- {#if app . logoUri }
497- <img src ={app . logoUri } alt ={app .name } class =" avatar" />
498+ {#if appLogoUrl }
499+ <img src ={appLogoUrl } alt ={app .name } class =" avatar" />
498500 {:else }
499501 <div class ="avatar placeholder" >{appInitial }</div >
500502 {/if }
902904 width : 3.5rem ;
903905 height : 3.5rem ;
904906 border-radius : var (--border-radius-l , 0.85rem );
905- object-fit : cover ;
906907 flex-shrink : 0 ;
907908 border : 1px solid var (--border-neutral-strong );
908909 background : var (--bgcolor-neutral-primary );
909910 }
910911
912+ /* Keep wordmarks readable inside the square avatar (cover crops them). */
913+ img .avatar {
914+ object-fit : contain ;
915+ padding : 0.4rem ;
916+ box-sizing : border-box ;
917+ }
918+
911919 .avatar.placeholder {
912920 display : flex ;
913921 align-items : center ;
Original file line number Diff line number Diff line change 33 import { Card , Typography , Icon } from ' @appwrite.io/pink-svelte' ;
44 import { IconCheck , IconX , IconLockClosed } from ' @appwrite.io/pink-icons-svelte' ;
55 import { Button } from ' $lib/elements/forms' ;
6+ import { resolveOAuth2AppLogoUrl } from ' $lib/helpers/oauth2-app-logo' ;
67 import type { OAuth2Flow , OAuth2Outcome } from ' ./consent-card.svelte' ;
78
89 interface Props {
2829
2930 const approved = $derived (outcome === ' approved' );
3031 const appName = $derived (app ?.name ?? ' the application' );
32+ const appLogoUrl = $derived (resolveOAuth2AppLogoUrl (app ));
3133 const appInitial = $derived ((app ?.name || ' ?' ).charAt (0 ).toUpperCase ());
3234 const accountInitial = $derived ((accountLabel || ' ?' ).charAt (0 ).toUpperCase ());
3335
5355 <div class =" outcome" class:approved >
5456 <header class =" header" >
5557 <div class =" identity" >
56- {#if app ?. logoUri }
57- <img src ={app . logoUri } alt ={appName } class =" avatar" />
58+ {#if appLogoUrl }
59+ <img src ={appLogoUrl } alt ={appName } class =" avatar" />
5860 {:else }
5961 <div class ="avatar placeholder" >{appInitial }</div >
6062 {/if }
158160 width : 3.5rem ;
159161 height : 3.5rem ;
160162 border-radius : var (--border-radius-l , 0.85rem );
161- object-fit : cover ;
162163 flex-shrink : 0 ;
163164 border : 1px solid var (--border-neutral-strong );
164165 background : var (--bgcolor-neutral-primary );
165166 }
166167
168+ /* Keep wordmarks readable inside the square avatar (cover crops them). */
169+ img .avatar {
170+ object-fit : contain ;
171+ padding : 0.4rem ;
172+ box-sizing : border-box ;
173+ }
174+
167175 .avatar.placeholder {
168176 display : flex ;
169177 align-items : center ;
You can’t perform that action at this time.
0 commit comments