55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 " />
66 < title > GitHub Co-Author Generator</ title >
77 < meta name ="description " content ="Generate Git Co-authored-by lines from GitHub profiles easily. " />
8- < link rel ="preconnect " href ="https://fonts.googleapis.com " />
9- < link rel ="preconnect " href ="https://fonts.gstatic.com " crossorigin />
10- < link
11- href ="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap "
12- rel ="stylesheet "
13- />
14- < style >
15- : root {
16- --primary : # ff9900 ;
17- --primary-hover : # ec7211 ;
18- --bg : # f2f3f3 ;
19- --card-bg : # ffffff ;
20- --text : # 16191f ;
21- --text-muted : # 545b64 ;
22- --border : # eaeded ;
23- --error : # df3312 ;
24- --header-bg : # 232f3e ;
25- --shadow : 0 1px 1px 0 rgba (0 , 28 , 36 , 0.1 ), 0 1px 1px 0 rgba (0 , 28 , 36 , 0.15 );
26- }
27-
28- * {
29- box-sizing : border-box;
30- margin : 0 ;
31- padding : 0 ;
32- }
33-
34- body {
35- font-family : "Inter" , system-ui, -apple-system, sans-serif;
36- background-color : var (--bg );
37- color : var (--text );
38- min-height : 100vh ;
39- display : flex;
40- flex-direction : column;
41- }
42-
43- .main-content {
44- flex : 1 ;
45- padding : 2rem ;
46- display : flex;
47- justify-content : center;
48- }
49-
50- .container {
51- width : 100% ;
52- max-width : 900px ;
53- background : var (--card-bg );
54- border : 1px solid var (--border );
55- border-radius : 2px ;
56- padding : 2.5rem ;
57- box-shadow : var (--shadow );
58- animation : fadeIn 0.4s ease-out;
59- }
60-
61- @keyframes fadeIn {
62- from {
63- opacity : 0 ;
64- }
65- to {
66- opacity : 1 ;
67- }
68- }
69-
70- h1 {
71- font-size : 1.75rem ;
72- font-weight : 700 ;
73- margin-bottom : 0.75rem ;
74- color : var (--text );
75- }
76-
77- p .subtitle {
78- color : var (--text-muted );
79- margin-bottom : 2rem ;
80- font-size : 0.95rem ;
81- line-height : 1.5 ;
82- }
83-
84- .form-section {
85- border-top : 1px solid var (--border );
86- padding-top : 2rem ;
87- }
88-
89- .form-group {
90- margin-bottom : 2rem ;
91- }
92-
93- label {
94- display : block;
95- font-size : 0.9rem ;
96- font-weight : 700 ;
97- margin-bottom : 0.5rem ;
98- color : var (--text );
99- }
100-
101- .input-row {
102- display : flex;
103- gap : 1rem ;
104- }
105-
106- input [type = "text" ] {
107- flex : 1 ;
108- background : # fff ;
109- border : 1px solid # 879596 ;
110- border-radius : 2px ;
111- padding : 0.5rem 0.75rem ;
112- color : var (--text );
113- font-size : 0.9rem ;
114- font-family : inherit;
115- transition : border-color 0.2s ;
116- outline : none;
117- }
118-
119- input [type = "text" ]: focus {
120- border-color : var (--primary );
121- box-shadow : 0 0 0 1px var (--primary );
122- }
123-
124- button {
125- background : var (--primary );
126- color : # fff ;
127- border : none;
128- border-radius : 2px ;
129- padding : 0.5rem 1.5rem ;
130- font-size : 0.9rem ;
131- font-weight : 700 ;
132- cursor : pointer;
133- transition : background 0.2s ;
134- display : flex;
135- align-items : center;
136- justify-content : center;
137- min-width : 120px ;
138- }
139-
140- button : hover {
141- background : var (--primary-hover );
142- }
143-
144- button : active {
145- background : # d46300 ;
146- }
147-
148- button : disabled {
149- background : # eaeded ;
150- color : # aab7b8 ;
151- cursor : not-allowed;
152- }
153-
154- .result-container {
155- margin-top : 2rem ;
156- padding-top : 2rem ;
157- border-top : 1px solid var (--border );
158- }
159-
160- .result-header {
161- display : flex;
162- justify-content : space-between;
163- align-items : center;
164- margin-bottom : 0.75rem ;
165- }
166-
167- .result-label {
168- font-size : 0.9rem ;
169- font-weight : 700 ;
170- color : var (--text );
171- }
172-
173- ace-editor {
174- border-radius : 2px ;
175- overflow : hidden;
176- border : 1px solid # 879596 ;
177- height : 100px ;
178- font-size : 14px ;
179- }
180-
181- .loading-spinner {
182- width : 1rem ;
183- height : 1rem ;
184- border : 2px solid rgba (255 , 255 , 255 , 0.3 );
185- border-top-color : white;
186- border-radius : 50% ;
187- animation : spin 0.6s linear infinite;
188- margin-right : 0.5rem ;
189- display : none;
190- }
191-
192- @keyframes spin {
193- to {
194- transform : rotate (360deg );
195- }
196- }
197-
198- .error-message {
199- color : var (--error );
200- font-size : 0.85rem ;
201- margin-top : 1rem ;
202- padding : 0.75rem 1rem ;
203- background : # fdf3f1 ;
204- border : 1px solid var (--error );
205- border-radius : 2px ;
206- display : none;
207- font-weight : 500 ;
208- }
209-
210- @media (max-width : 640px ) {
211- .input-row {
212- flex-direction : column;
213- }
214- button {
215- width : 100% ;
216- }
217- }
218- </ style >
2198 </ head >
220- < body >
221- < main class ="main-content ">
222- < div class ="container ">
9+ < body class =" layout bg " >
10+ < div class ="body ">
11+ < div class ="inside ">
22312 < h1 > GitHub Co-Author Generator</ h1 >
224- < p class =" subtitle " >
13+ < p >
22514 Quickly generate Git attribution lines from GitHub profile URLs. This tool fetches user IDs and formats them
22615 for commit messages.
22716 </ p >
22817
229- < div class ="form-section ">
230- < div class ="form-group ">
231- < label for ="github-url "> GitHub user URL</ label >
232- < div class ="input-row ">
233- < input type ="text " id ="github-url " placeholder ="https://github.com/username " autocomplete ="off " />
234- < button id ="generate-btn ">
235- < span class ="loading-spinner " id ="spinner "> </ span >
236- < span > Generate</ span >
237- </ button >
238- </ div >
239- < div id ="error-box " class ="error-message "> </ div >
240- </ div >
241- </ div >
242- < div class ="result-container ">
243- < div class ="result-header ">
244- < span class ="result-label "> Result</ span >
245- </ div >
246- < ace-editor id ="result-editor " lang ="python " min-height-px ="600 ">
247- < script type ="ace "> </ script >
248- </ ace-editor >
18+ < div style ="display: flex; gap: 10px; margin-bottom: 2rem; margin-top: 2rem ">
19+ < input
20+ type ="text "
21+ id ="github-url "
22+ placeholder ="https://github.com/username "
23+ autocomplete ="off "
24+ style ="flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; font-size: 1rem "
25+ />
26+ < button
27+ id ="generate-btn "
28+ style ="
29+ padding: 10px 20px;
30+ background: #0366d6;
31+ color: white;
32+ border: none;
33+ border-radius: 4px;
34+ cursor: pointer;
35+ font-weight: 600;
36+ font-size: 1rem;
37+ "
38+ >
39+ Generate
40+ </ button >
24941 </ div >
42+
43+ < ace-editor id ="result-editor " lang ="python " min-height-px ="200 ">
44+ < script type ="ace "> </ script >
45+ </ ace-editor >
25046 </ div >
251- </ main >
47+ </ div >
25248
253- < script type =" module " >
254- const resultEditor = document . getElementById ( "result-editor" ) ;
49+ <!-- Core Project Logic -- >
50+ < script type =" module " src =" /js/github.js " > </ script >
25551
256- // Wrap onLoad event in a promise
257- await new Promise ( ( resolve ) => {
258- resultEditor . addEventListener ( "onLoad" , resolve ) ;
259- } ) ;
52+ <!-- Ace Editor Web Component -->
53+ < script
54+ type ="module "
55+ src ="https://stopsopa.github.io/ace-editor-webcomponent/ace-web-component.js "
56+ data-main-ace ="https://stopsopa.github.io/ace-editor-webcomponent/noprettier/ace/ace-builds-1.43.4/src-min-noconflict/ace.js "
57+ > </ script >
26058
59+ < script type ="module ">
60+ const resultEditor = document . getElementById ( "result-editor" ) ;
26161 const urlInput = document . getElementById ( "github-url" ) ;
26262 const generateBtn = document . getElementById ( "generate-btn" ) ;
263- const spinner = document . getElementById ( "spinner" ) ;
264- const errorBox = document . getElementById ( "error-box" ) ;
265-
266- function showError ( msg ) {
267- errorBox . textContent = msg ;
268- errorBox . style . display = "block" ;
269- resultEditor . value = `\nError: ${ msg } \n` ;
270- }
271-
272- function clearError ( ) {
273- errorBox . textContent = "" ;
274- errorBox . style . display = "none" ;
275- }
276-
277- function setURLParam ( url ) {
278- const newUrl = new URL ( window . location . href ) ;
279- newUrl . searchParams . set ( "url" , url ) ;
280- window . history . replaceState ( { } , "" , newUrl ) ;
281- }
28263
28364 async function generate ( ) {
28465 const rawUrl = urlInput . value . trim ( ) ;
28566 if ( ! rawUrl ) return ;
28667
287- // Clear previous results
288- clearError ( ) ;
28968 resultEditor . value = "\nFetching user data...\n" ;
29069 generateBtn . disabled = true ;
291- spinner . style . display = "block" ;
29270
29371 try {
294- // Update URL parameter
295- setURLParam ( rawUrl ) ;
296-
29772 // Extract username
298- // Remove trailing slashes and get the last segment
29973 const username = rawUrl . replace ( / \/ + $ / , "" ) . split ( "/" ) . pop ( ) ;
30074
30175 if ( ! username || username === "github.com" ) {
@@ -315,22 +89,15 @@ <h1>GitHub Co-Author Generator</h1>
31589
31690 const coAuthorLine = `Co-authored-by: ${ data . login } <${ data . id } +${ data . login } @users.noreply.github.com>` ;
31791 resultEditor . value = `\n${ coAuthorLine } \n` ;
92+
93+ // Update URL parameter
94+ const newUrl = new URL ( window . location . href ) ;
95+ newUrl . searchParams . set ( "url" , rawUrl ) ;
96+ window . history . replaceState ( { } , "" , newUrl ) ;
31897 } catch ( err ) {
319- showError ( err . message ) ;
98+ resultEditor . value = `\nError: ${ err . message } \n` ;
32099 } finally {
321100 generateBtn . disabled = false ;
322- spinner . style . display = "none" ;
323- }
324- }
325-
326- // Initialize from URL parameter
327- function init ( ) {
328- const params = new URLSearchParams ( window . location . search ) ;
329- const urlParam = params . get ( "url" ) ;
330-
331- if ( urlParam ) {
332- urlInput . value = urlParam ;
333- generate ( ) ;
334101 }
335102 }
336103
@@ -342,15 +109,16 @@ <h1>GitHub Co-Author Generator</h1>
342109 }
343110 } ) ;
344111
345- // Start initialization
346- init ( ) ;
347- </ script >
112+ // Wait for editor potentially to be ready, but web component handles early value setting too
113+ const params = new URLSearchParams ( window . location . search ) ;
114+ const urlParam = params . get ( "url" ) ;
348115
349- <!-- Ace Editor Web Component -->
350- < script
351- type ="module "
352- src ="https://stopsopa.github.io/ace-editor-webcomponent/ace-web-component.js "
353- data-main-ace ="https://stopsopa.github.io/ace-editor-webcomponent/noprettier/ace/ace-builds-1.43.4/src-min-noconflict/ace.js "
354- > </ script >
116+ if ( urlParam ) {
117+ urlInput . value = urlParam ;
118+ // Small delay to ensure editor can receive the first update if needed,
119+ // though the web component is designed to handle it.
120+ setTimeout ( generate , 100 ) ;
121+ }
122+ </ script >
355123 </ body >
356124</ html >
0 commit comments