@@ -18,24 +18,25 @@ function getEdgePath(): string {
1818}
1919
2020function getEdgeUserDataDir ( ) : string {
21+ // Use a dedicated profile directory to avoid WebView2 conflicts
22+ // Extensions need to be installed once in this profile
2123 if ( process . platform === 'win32' ) {
2224 return path . join (
2325 os . homedir ( ) ,
2426 'AppData' ,
2527 'Local' ,
26- 'Microsoft' ,
27- 'Edge' ,
28- 'User Data'
28+ 'GH-SocialPreviewGenerator' ,
29+ 'EdgeProfile'
2930 )
3031 } else if ( process . platform === 'darwin' ) {
3132 return path . join (
3233 os . homedir ( ) ,
3334 'Library' ,
3435 'Application Support' ,
35- 'Microsoft Edge '
36+ 'GH-SocialPreviewGenerator '
3637 )
3738 } else {
38- return path . join ( os . homedir ( ) , '.config' , 'microsoft-edge ' )
39+ return path . join ( os . homedir ( ) , '.config' , 'gh-social-preview-generator ' )
3940 }
4041}
4142
@@ -47,9 +48,10 @@ export async function uploadSocialPreviewViaBrowser(
4748 const browser = await puppeteer . launch ( {
4849 executablePath : getEdgePath ( ) ,
4950 userDataDir : getEdgeUserDataDir ( ) ,
50- headless : false , // Need to be visible for user to see progress
51+ headless : false ,
5152 defaultViewport : null ,
52- args : [ '--no-first-run' , '--no-default-browser-check' ]
53+ args : [ '--no-first-run' , '--no-default-browser-check' ] ,
54+ ignoreDefaultArgs : [ '--disable-extensions' ]
5355 } )
5456
5557 try {
@@ -59,40 +61,85 @@ export async function uploadSocialPreviewViaBrowser(
5961 const settingsUrl = `https://github.com/${ owner } /${ repo } /settings`
6062 await page . goto ( settingsUrl , { waitUntil : 'networkidle2' } )
6163
62- // Wait for the page to load and find the social preview section
63- // The "Edit" button is in the Social preview section
64- await page . waitForSelector ( 'details-dialog input[type="file"]' , {
65- timeout : 10000
64+ // Check if we're logged in by looking for the settings form
65+ const settingsForm = await page . $ ( 'form[data-turbo="false"], main h2' )
66+ if ( ! settingsForm ) {
67+ console . log ( '\n Please log into GitHub in the browser window...' )
68+ console . log ( ' (Waiting up to 2 minutes for login)\n' )
69+ await page . waitForSelector ( 'form[data-turbo="false"], main h2' , {
70+ timeout : 120000
71+ } )
72+ }
73+
74+ await delay ( 1000 )
75+
76+ // Scroll down to find Social preview section
77+ await page . evaluate ( ( ) => {
78+ const labels = document . querySelectorAll ( 'label, h3, summary' )
79+ for ( const label of labels ) {
80+ if ( label . textContent ?. includes ( 'Social preview' ) ) {
81+ label . scrollIntoView ( { behavior : 'smooth' , block : 'center' } )
82+ break
83+ }
84+ }
6685 } )
6786
68- // Find and click the Edit button for social preview
69- const editButton = await page . $ ( 'button[aria-label="Edit social preview"]' )
70- if ( editButton ) {
71- await editButton . click ( )
72- await page . waitForSelector ( 'input[type="file"]' , {
73- visible : true ,
74- timeout : 5000
75- } )
87+ await delay ( 1000 )
88+
89+ // Click the Edit button or summary to expand the social preview section
90+ const editClicked = await page . evaluate ( ( ) => {
91+ const summaries = document . querySelectorAll ( 'summary, button' )
92+ for ( const el of summaries ) {
93+ if ( el . textContent ?. includes ( 'Edit' ) && el . closest ( '[class*="social"]' ) ) {
94+ ; ( el as HTMLElement ) . click ( )
95+ return true
96+ }
97+ }
98+ // Try clicking any Edit button near Social preview text
99+ const labels = document . querySelectorAll ( 'label, h3' )
100+ for ( const label of labels ) {
101+ if ( label . textContent ?. includes ( 'Social preview' ) ) {
102+ const parent = label . closest ( 'div' )
103+ const btn = parent ?. querySelector ( 'summary, button' )
104+ if ( btn ) {
105+ ; ( btn as HTMLElement ) . click ( )
106+ return true
107+ }
108+ }
109+ }
110+ return false
111+ } )
112+
113+ if ( editClicked ) {
114+ await delay ( 1000 )
115+ }
116+
117+ // Find the file input (might be hidden, use a more general selector)
118+ let fileInput = await page . $ ( 'input[type="file"]' )
119+ if ( ! fileInput ) {
120+ // Wait a bit more for it to appear
121+ await page . waitForSelector ( 'input[type="file"]' , { timeout : 5000 } )
122+ fileInput = await page . $ ( 'input[type="file"]' )
76123 }
77124
78- // Upload the file
79- const fileInput = await page . $ ( 'input[type="file"]' )
80125 if ( ! fileInput ) {
81126 throw new Error ( 'Could not find file input for social preview upload' )
82127 }
83128
84129 await fileInput . uploadFile ( imagePath )
130+ await delay ( 3000 )
85131
86- // Wait for upload to process
87- await delay ( 2000 )
88-
89- // Click the Save/Update button
90- const saveButton = await page . $ (
91- 'button[type="submit"]:has-text("Save"), button:has-text("Update social preview")'
92- )
93- if ( saveButton ) {
94- await saveButton . click ( )
95- await delay ( 2000 )
132+ // Look for and click save button
133+ const buttons = await page . $$ ( 'button[type="submit"], button' )
134+ for ( const button of buttons ) {
135+ const text = await button . evaluate (
136+ el => el . textContent ?. toLowerCase ( ) || ''
137+ )
138+ if ( text . includes ( 'save' ) || text . includes ( 'update' ) ) {
139+ await button . click ( )
140+ await delay ( 2000 )
141+ break
142+ }
96143 }
97144
98145 await page . close ( )
@@ -110,7 +157,8 @@ export async function uploadAllViaBrowser(
110157 userDataDir : getEdgeUserDataDir ( ) ,
111158 headless : false ,
112159 defaultViewport : null ,
113- args : [ '--no-first-run' , '--no-default-browser-check' ]
160+ args : [ '--no-first-run' , '--no-default-browser-check' ] ,
161+ ignoreDefaultArgs : [ '--disable-extensions' ]
114162 } )
115163
116164 let success = 0
@@ -134,57 +182,91 @@ export async function uploadAllViaBrowser(
134182 // Look for the social preview edit button or file input
135183 // GitHub's settings page has a "Social preview" section with an Edit button
136184
137- // Wait for page to be ready
138- await page . waitForSelector ( 'main' , { timeout : 10000 } )
185+ // Check if we're logged in by looking for the settings form
186+ const settingsForm = await page . $ ( 'form[data-turbo="false"], main h2' )
187+ if ( ! settingsForm ) {
188+ console . log ( '\n Please log into GitHub in the browser window...' )
189+ console . log ( ' (Waiting up to 2 minutes for login)\n' )
190+ await page . waitForSelector ( 'form[data-turbo="false"], main h2' , {
191+ timeout : 120000
192+ } )
193+ }
194+
195+ await delay ( 1000 )
139196
140- // Scroll to social preview section if needed
197+ // Scroll down to find Social preview section
141198 await page . evaluate ( ( ) => {
142- const heading = Array . from (
143- document . querySelectorAll ( 'h2, h3, label' )
144- ) . find ( el => el . textContent ?. includes ( 'Social preview' ) )
145- if ( heading ) {
146- heading . scrollIntoView ( { behavior : 'smooth' , block : 'center' } )
199+ const labels = document . querySelectorAll ( 'label, h3, summary' )
200+ for ( const label of labels ) {
201+ if ( label . textContent ?. includes ( 'Social preview' ) ) {
202+ label . scrollIntoView ( { behavior : 'smooth' , block : 'center' } )
203+ break
204+ }
147205 }
148206 } )
149207
150- await delay ( 500 )
208+ await delay ( 1000 )
151209
152- // Click Edit button if there's a summary/details element
153- const editButton = await page . $ (
154- 'button[aria-label="Edit repository image"]'
155- )
156- if ( editButton ) {
157- await editButton . click ( )
158- await delay ( 500 )
210+ // Click the Edit button or summary to expand the social preview section
211+ const editClicked = await page . evaluate ( ( ) => {
212+ const summaries = document . querySelectorAll ( 'summary, button' )
213+ for ( const el of summaries ) {
214+ if (
215+ el . textContent ?. includes ( 'Edit' ) &&
216+ el . closest ( '[class*="social"]' )
217+ ) {
218+ ; ( el as HTMLElement ) . click ( )
219+ return true
220+ }
221+ }
222+ // Try clicking any Edit button near Social preview text
223+ const labels = document . querySelectorAll ( 'label, h3' )
224+ for ( const label of labels ) {
225+ if ( label . textContent ?. includes ( 'Social preview' ) ) {
226+ const parent = label . closest ( 'div' )
227+ const btn = parent ?. querySelector ( 'summary, button' )
228+ if ( btn ) {
229+ ; ( btn as HTMLElement ) . click ( )
230+ return true
231+ }
232+ }
233+ }
234+ return false
235+ } )
236+
237+ if ( editClicked ) {
238+ await delay ( 1000 )
159239 }
160240
161- // Find the file input (might be hidden)
162- const fileInput = await page . $ ( 'input[type="file"][accept*="image "]' )
241+ // Find the file input (might be hidden, use a more general selector )
242+ let fileInput = await page . $ ( 'input[type="file"]' )
163243 if ( ! fileInput ) {
164- throw new Error ( 'Could not find file input' )
244+ // Wait a bit more for it to appear
245+ await page . waitForSelector ( 'input[type="file"]' , { timeout : 5000 } )
246+ fileInput = await page . $ ( 'input[type="file"]' )
165247 }
166248
167- // Upload the file
168- await fileInput . uploadFile ( imagePath )
249+ if ( ! fileInput ) {
250+ throw new Error ( 'Could not find file input for social preview upload' )
251+ }
169252
170- // Wait for upload to complete
253+ await fileInput . uploadFile ( imagePath )
171254 await delay ( 3000 )
172255
173- // Look for and click save/submit button
174- const buttons = await page . $$ (
175- 'button[type="submit"], button[type="button"]'
176- )
256+ // Look for and click save button
257+ const buttons = await page . $$ ( 'button[type="submit"], button' )
177258 for ( const button of buttons ) {
178259 const text = await button . evaluate (
179260 el => el . textContent ?. toLowerCase ( ) || ''
180261 )
181262 if ( text . includes ( 'save' ) || text . includes ( 'update' ) ) {
182263 await button . click ( )
264+ await delay ( 2000 )
183265 break
184266 }
185267 }
186268
187- await delay ( 2000 )
269+ await delay ( 1000 )
188270 success ++
189271 } catch ( error ) {
190272 console . error (
0 commit comments