detached 96cccd7

feat: Localize UI and enhance user experience with French translations

- Update aim-login component with French labels and placeholders
- Modify chat-room component to display French error messages and status texts
- Update desktop-icons with French alt texts and labels
- Add French translations to loading components
- Implement language-specific UI improvements
commit
96cccd78a1a0c453a6f2d8f7706d9cc5d5211d04
author
Kit · · 2025-02-16 08:10 UTC
parent
944de91
signature
SSH UnverifiedSHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6Tk The key is unknown
6 files · +41 -44
M .cursor/rules/global.mdc +5 -2
@@ -3,5 +3,8 @@ description:
33 globs:
44 ---
55 # Debugging
6-- Always add console.debug for client sided code
7-- Always add either console.log or follow the logger implementation logic is there is one for server/backend code⏎̸
6+- Always add console.debug for client sided code .
7+- Always add either console.log or follow the logger implementation if any server/backend code.
8+
9+# Language
10+- The content of the app is in French but the code comments must be in English.⏎̸
@@ -3,5 +3,8 @@ description:
3 globs: 3 globs:
4 ---4 ---
5 # Debugging5 # Debugging
6-- Always add console.debug for client sided code 6+- Always add console.debug for client sided code .
7-- Always add either console.log or follow the logger implementation logic is there is one for server/backend code⏎̸7+- Always add either console.log or follow the logger implementation if any server/backend code.
8+
9+# Language
10+- The content of the app is in French but the code comments must be in English.⏎̸
M src/lib/components/aim-login.svelte +17 -17
@@ -179,7 +179,7 @@
179179 <div class="title-bar">
180180 <div class="title-bar-text">
181181 <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" />
182- {activeTab === 'signin' ? 'Sign In' : 'Sign Up'}
182+ {activeTab === 'signin' ? 'Connexion' : 'Inscription'}
183183 </div>
184184 <div class="title-bar-controls">
185185 <button aria-label="Close" onclick={handleClose}>×</button>
@@ -187,20 +187,20 @@
187187 </div>
188188
189189 <div class="tabs">
190- <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Sign In</button>
191- <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Sign Up</button>
190+ <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Connexion</button>
191+ <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Inscription</button>
192192 </div>
193193
194194 <div class="window-content">
195195 {#if activeTab === 'signin'}
196196 <form onsubmit={handleSigninSubmit}>
197197 <div class="form-group">
198- <label for="si-username">Username</label>
199- <input type="text" id="si-username" bind:value={siUsername} placeholder="Username" />
198+ <label for="si-username">Nom d'utilisateur</label>
199+ <input type="text" id="si-username" bind:value={siUsername} placeholder="Nom d'utilisateur" />
200200 </div>
201201 <div class="form-group">
202- <label for="si-password">Password</label>
203- <input type="password" id="si-password" bind:value={siPassword} placeholder="Password" />
202+ <label for="si-password">Mot de passe</label>
203+ <input type="password" id="si-password" bind:value={siPassword} placeholder="Mot de passe" />
204204 </div>
205205 {#if error}
206206 <div class="error">{error}</div>
@@ -229,27 +229,27 @@
229229 <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/>
230230 </svg>
231231 {/if}
232- Sign In
232+ Se connecter
233233 </button>
234234 </div>
235235 </form>
236236 {:else}
237237 <form onsubmit={handleSignupSubmit}>
238238 <div class="form-group">
239- <label for="su-username">Username</label>
240- <input type="text" id="su-username" bind:value={suUsername} placeholder="Choose a username" />
239+ <label for="su-username">Nom d'utilisateur</label>
240+ <input type="text" id="su-username" bind:value={suUsername} placeholder="Choisissez un nom d'utilisateur" />
241241 </div>
242242 <div class="form-group">
243- <label for="su-password">Password</label>
244- <input type="password" id="su-password" bind:value={suPassword} placeholder="Password" />
243+ <label for="su-password">Mot de passe</label>
244+ <input type="password" id="su-password" bind:value={suPassword} placeholder="Mot de passe" />
245245 </div>
246246 <div class="form-group">
247- <label for="su-confirm-password">Confirm Password</label>
248- <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirm Password" />
247+ <label for="su-confirm-password">Confirmer le mot de passe</label>
248+ <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirmer le mot de passe" />
249249 </div>
250250 <div class="form-group">
251- <label for="captcha">What's the meaning of PDR?</label>
252- <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Enter your answer" />
251+ <label for="captcha">Quelle est la signification de PDR ?</label>
252+ <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Entrez votre réponse" />
253253 </div>
254254 {#if signupError}
255255 <div class="error">{signupError}</div>
@@ -278,7 +278,7 @@
278278 <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path>
279279 </svg>
280280 {/if}
281- Sign Up
281+ S'inscrire
282282 </button>
283283 </div>
284284 </form>
@@ -179,7 +179,7 @@
179 <div class="title-bar">179 <div class="title-bar">
180 <div class="title-bar-text">180 <div class="title-bar-text">
181 <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" />181 <img src="/desktop/aim-desktop-icon.png" alt="AIM" class="title-icon" width="16" height="16" />
182- {activeTab === 'signin' ? 'Sign In' : 'Sign Up'}182+ {activeTab === 'signin' ? 'Connexion' : 'Inscription'}
183 </div>183 </div>
184 <div class="title-bar-controls">184 <div class="title-bar-controls">
185 <button aria-label="Close" onclick={handleClose}>×</button>185 <button aria-label="Close" onclick={handleClose}>×</button>
@@ -187,20 +187,20 @@
187 </div>187 </div>
188 188
189 <div class="tabs">189 <div class="tabs">
190- <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Sign In</button>190+ <button class:active={activeTab === 'signin'} onclick={() => switchTab('signin')}>Connexion</button>
191- <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Sign Up</button>191+ <button class:active={activeTab === 'signup'} onclick={() => switchTab('signup')}>Inscription</button>
192 </div>192 </div>
193 193
194 <div class="window-content">194 <div class="window-content">
195 {#if activeTab === 'signin'}195 {#if activeTab === 'signin'}
196 <form onsubmit={handleSigninSubmit}>196 <form onsubmit={handleSigninSubmit}>
197 <div class="form-group">197 <div class="form-group">
198- <label for="si-username">Username</label>198+ <label for="si-username">Nom d'utilisateur</label>
199- <input type="text" id="si-username" bind:value={siUsername} placeholder="Username" />199+ <input type="text" id="si-username" bind:value={siUsername} placeholder="Nom d'utilisateur" />
200 </div>200 </div>
201 <div class="form-group">201 <div class="form-group">
202- <label for="si-password">Password</label>202+ <label for="si-password">Mot de passe</label>
203- <input type="password" id="si-password" bind:value={siPassword} placeholder="Password" />203+ <input type="password" id="si-password" bind:value={siPassword} placeholder="Mot de passe" />
204 </div>204 </div>
205 {#if error}205 {#if error}
206 <div class="error">{error}</div>206 <div class="error">{error}</div>
@@ -229,27 +229,27 @@
229 <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/>229 <path d="M12 4C9.24 4 7 6.24 7 9c0 2.76 2.24 5 5 5s5-2.24 5-5c0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 10c-3.33 0-7 1.67-7 5v1h14v-1c0-3.33-3.67-5-7-5zm0 2c2.67 0 5 1.33 5 3H7c0-1.67 2.33-3 5-3z" fill="currentColor"/>
230 </svg>230 </svg>
231 {/if}231 {/if}
232- Sign In232+ Se connecter
233 </button>233 </button>
234 </div>234 </div>
235 </form>235 </form>
236 {:else}236 {:else}
237 <form onsubmit={handleSignupSubmit}>237 <form onsubmit={handleSignupSubmit}>
238 <div class="form-group">238 <div class="form-group">
239- <label for="su-username">Username</label>239+ <label for="su-username">Nom d'utilisateur</label>
240- <input type="text" id="su-username" bind:value={suUsername} placeholder="Choose a username" />240+ <input type="text" id="su-username" bind:value={suUsername} placeholder="Choisissez un nom d'utilisateur" />
241 </div>241 </div>
242 <div class="form-group">242 <div class="form-group">
243- <label for="su-password">Password</label>243+ <label for="su-password">Mot de passe</label>
244- <input type="password" id="su-password" bind:value={suPassword} placeholder="Password" />244+ <input type="password" id="su-password" bind:value={suPassword} placeholder="Mot de passe" />
245 </div>245 </div>
246 <div class="form-group">246 <div class="form-group">
247- <label for="su-confirm-password">Confirm Password</label>247+ <label for="su-confirm-password">Confirmer le mot de passe</label>
248- <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirm Password" />248+ <input type="password" id="su-confirm-password" bind:value={suConfirmPassword} placeholder="Confirmer le mot de passe" />
249 </div>249 </div>
250 <div class="form-group">250 <div class="form-group">
251- <label for="captcha">What's the meaning of PDR?</label>251+ <label for="captcha">Quelle est la signification de PDR ?</label>
252- <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Enter your answer" />252+ <input type="text" id="captcha" bind:value={captchaAnswer} placeholder="Entrez votre réponse" />
253 </div>253 </div>
254 {#if signupError}254 {#if signupError}
255 <div class="error">{signupError}</div>255 <div class="error">{signupError}</div>
@@ -278,7 +278,7 @@
278 <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path>278 <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"></path>
279 </svg>279 </svg>
280 {/if}280 {/if}
281- Sign Up281+ S'inscrire
282 </button>282 </button>
283 </div>283 </div>
284 </form>284 </form>
M src/lib/components/chat-room.svelte +13 -19
@@ -369,6 +369,9 @@ $effect(() => {
369369 isInitialLoading = false;
370370 }
371371 });
372+
373+// Add derived state for total users count
374+let totalUsers = $derived(onlineUsers.length);
372375 </script>
373376
374377 {#if showChatRoom}
@@ -393,9 +396,9 @@ $effect(() => {
393396 >
394397 <div class="title-bar">
395398 <div class="title-bar-text">
396- Pdr Aim - {currentUser?.nickname}
399+ Pdr Aim {#if currentUser} - {currentUser.nickname}{:else} - {totalUsers} personne{totalUsers > 1 ? 's' : ''} présente{totalUsers > 1 ? 's' : ''}{/if}
397400 {#if sseError}
398- <span class="connection-error">⚠️ Connection Error</span>
401+ <span class="connection-error">⚠️ Erreur de connexion</span>
399402 {/if}
400403 </div>
401404 <div class="title-bar-controls">
@@ -409,7 +412,7 @@ $effect(() => {
409412 <button onclick={handleClose} aria-label="Close"></button>
410413 {:else}
411414 <button
412- aria-label="Toggle Buddy List"
415+ aria-label="Afficher/Masquer la liste des contacts"
413416 onclick={() => showUserList = !showUserList}
414417 >👥</button>
415418 {/if}
@@ -417,25 +420,22 @@ $effect(() => {
417420 </div>
418421
419422 <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;">
420- <!-- Add error message display -->
421423 {#if sseError}
422424 <div class="error-banner">
423425 {sseError}
424426 {#if sseRetryAfter}
425427 <br>
426- <small>Retrying in {Math.ceil(sseRetryAfter)}s...</small>
428+ <small>Nouvelle tentative dans {Math.ceil(sseRetryAfter)}s...</small>
427429 {/if}
428430 </div>
429431 {/if}
430432
431- <!-- Chat content area -->
432433 <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;">
433- <!-- Add rate limit warning -->
434434 {#if rateLimitWarning}
435435 <div class="rate-limit-warning" class:with-progress={cooldownEndTime}>
436436 <span>{rateLimitWarning}</span>
437437 {#if cooldownEndTime}
438- <small>You can send another message in {cooldownProgress.toFixed(1)}s</small>
438+ <small>Vous pourrez envoyer un autre message dans {cooldownProgress.toFixed(1)}s</small>
439439 {/if}
440440 </div>
441441 {/if}
@@ -479,14 +479,14 @@ $effect(() => {
479479 bind:value={currentMessage}
480480 style="flex: 1;"
481481 onkeydown={(e) => e.key === 'Enter' && handleSubmit()}
482- placeholder={cooldownEndTime ? `Wait ${cooldownProgress.toFixed(1)}s...` : "Type a message..."}
482+ placeholder={cooldownEndTime ? `Patientez ${cooldownProgress.toFixed(1)}s...` : "Écrivez un message..."}
483483 disabled={!currentUser || Boolean(cooldownEndTime)}
484484 />
485485 <LoadingButton
486486 onclick={handleSubmit}
487487 disabled={!currentUser || Boolean(cooldownEndTime)}
488488 loading={isSendingMessage}
489- text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Send"}
489+ text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Envoyer"}
490490 />
491491 </div>
492492 {#if cooldownEndTime}
@@ -501,7 +501,7 @@ $effect(() => {
501501 class:hidden={isMobile && !showUserList}
502502 style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;"
503503 >
504- <p style="margin: 0 0 0.3rem 0;"><strong>Buddy List</strong></p>
504+ <p style="margin: 0 0 0.3rem 0;"><strong>Liste d'amis</strong></p>
505505 {#each onlineUsers as user}
506506 <div class="user" class:offline={user.status === 'offline'}>
507507 <span class="status-icon">{getStatusIcon(user.status)}</span>
@@ -513,11 +513,6 @@ $effect(() => {
513513 {:else}
514514 <div class="nickname select-none">{user.nickname}</div>
515515 {/if}
516- {#if user.status === 'away'}
517- <div class="status-message">Away</div>
518- {:else if user.status === 'busy'}
519- <div class="status-message">Busy</div>
520- {/if}
521516 </div>
522517 </div>
523518 {/each}
@@ -527,7 +522,6 @@ $effect(() => {
527522 {/if}
528523
529524 <style>
530-
531525 .chat-area, .users-list, input {
532526 font-size: 1rem;
533527 font-family: Arial, Verdana, Tahoma, sans-serif;
@@ -608,14 +602,14 @@ $effect(() => {
608602 color: #666;
609603 }
610604
611- .user .status-message {
605+ /* .user .status-message {
612606 font-size: 0.875rem;
613607 color: #666;
614608 font-style: italic;
615609 overflow: hidden;
616610 text-overflow: ellipsis;
617611 white-space: nowrap;
618- }
612+ } */
619613
620614 .sunken-panel {
621615 background: white;
@@ -369,6 +369,9 @@ $effect(() => {
369 isInitialLoading = false;369 isInitialLoading = false;
370 }370 }
371 });371 });
372+
373+// Add derived state for total users count
374+let totalUsers = $derived(onlineUsers.length);
372 </script>375 </script>
373 376
374 {#if showChatRoom}377 {#if showChatRoom}
@@ -393,9 +396,9 @@ $effect(() => {
393 >396 >
394 <div class="title-bar">397 <div class="title-bar">
395 <div class="title-bar-text">398 <div class="title-bar-text">
396- Pdr Aim - {currentUser?.nickname}399+ Pdr Aim {#if currentUser} - {currentUser.nickname}{:else} - {totalUsers} personne{totalUsers > 1 ? 's' : ''} présente{totalUsers > 1 ? 's' : ''}{/if}
397 {#if sseError}400 {#if sseError}
398- <span class="connection-error">⚠️ Connection Error</span>401+ <span class="connection-error">⚠️ Erreur de connexion</span>
399 {/if}402 {/if}
400 </div>403 </div>
401 <div class="title-bar-controls">404 <div class="title-bar-controls">
@@ -409,7 +412,7 @@ $effect(() => {
409 <button onclick={handleClose} aria-label="Close"></button>412 <button onclick={handleClose} aria-label="Close"></button>
410 {:else}413 {:else}
411 <button 414 <button
412- aria-label="Toggle Buddy List"415+ aria-label="Afficher/Masquer la liste des contacts"
413 onclick={() => showUserList = !showUserList}416 onclick={() => showUserList = !showUserList}
414 >👥</button>417 >👥</button>
415 {/if}418 {/if}
@@ -417,25 +420,22 @@ $effect(() => {
417 </div>420 </div>
418 421
419 <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;">422 <div class="window-body" style="display: flex; height: calc(100% - 2rem); margin: 0; padding: 0.5rem;">
420- <!-- Add error message display -->
421 {#if sseError}423 {#if sseError}
422 <div class="error-banner">424 <div class="error-banner">
423 {sseError}425 {sseError}
424 {#if sseRetryAfter}426 {#if sseRetryAfter}
425 <br>427 <br>
426- <small>Retrying in {Math.ceil(sseRetryAfter)}s...</small>428+ <small>Nouvelle tentative dans {Math.ceil(sseRetryAfter)}s...</small>
427 {/if}429 {/if}
428 </div>430 </div>
429 {/if}431 {/if}
430 432
431- <!-- Chat content area -->
432 <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;">433 <div class="chat-container" style="flex: 1; display: flex; flex-direction: column; margin-right: 0.5rem;">
433- <!-- Add rate limit warning -->
434 {#if rateLimitWarning}434 {#if rateLimitWarning}
435 <div class="rate-limit-warning" class:with-progress={cooldownEndTime}>435 <div class="rate-limit-warning" class:with-progress={cooldownEndTime}>
436 <span>{rateLimitWarning}</span>436 <span>{rateLimitWarning}</span>
437 {#if cooldownEndTime}437 {#if cooldownEndTime}
438- <small>You can send another message in {cooldownProgress.toFixed(1)}s</small>438+ <small>Vous pourrez envoyer un autre message dans {cooldownProgress.toFixed(1)}s</small>
439 {/if}439 {/if}
440 </div>440 </div>
441 {/if}441 {/if}
@@ -479,14 +479,14 @@ $effect(() => {
479 bind:value={currentMessage}479 bind:value={currentMessage}
480 style="flex: 1;"480 style="flex: 1;"
481 onkeydown={(e) => e.key === 'Enter' && handleSubmit()}481 onkeydown={(e) => e.key === 'Enter' && handleSubmit()}
482- placeholder={cooldownEndTime ? `Wait ${cooldownProgress.toFixed(1)}s...` : "Type a message..."}482+ placeholder={cooldownEndTime ? `Patientez ${cooldownProgress.toFixed(1)}s...` : "Écrivez un message..."}
483 disabled={!currentUser || Boolean(cooldownEndTime)}483 disabled={!currentUser || Boolean(cooldownEndTime)}
484 />484 />
485 <LoadingButton 485 <LoadingButton
486 onclick={handleSubmit} 486 onclick={handleSubmit}
487 disabled={!currentUser || Boolean(cooldownEndTime)} 487 disabled={!currentUser || Boolean(cooldownEndTime)}
488 loading={isSendingMessage}488 loading={isSendingMessage}
489- text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Send"}489+ text={cooldownEndTime ? `${cooldownProgress.toFixed(1)}s` : "Envoyer"}
490 />490 />
491 </div>491 </div>
492 {#if cooldownEndTime}492 {#if cooldownEndTime}
@@ -501,7 +501,7 @@ $effect(() => {
501 class:hidden={isMobile && !showUserList}501 class:hidden={isMobile && !showUserList}
502 style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;"502 style="width: {isMobile ? '100%' : '9.375rem'}; padding: 0.5rem; overflow-y: auto;"
503 >503 >
504- <p style="margin: 0 0 0.3rem 0;"><strong>Buddy List</strong></p>504+ <p style="margin: 0 0 0.3rem 0;"><strong>Liste d'amis</strong></p>
505 {#each onlineUsers as user}505 {#each onlineUsers as user}
506 <div class="user" class:offline={user.status === 'offline'}>506 <div class="user" class:offline={user.status === 'offline'}>
507 <span class="status-icon">{getStatusIcon(user.status)}</span>507 <span class="status-icon">{getStatusIcon(user.status)}</span>
@@ -513,11 +513,6 @@ $effect(() => {
513 {:else}513 {:else}
514 <div class="nickname select-none">{user.nickname}</div>514 <div class="nickname select-none">{user.nickname}</div>
515 {/if}515 {/if}
516- {#if user.status === 'away'}
517- <div class="status-message">Away</div>
518- {:else if user.status === 'busy'}
519- <div class="status-message">Busy</div>
520- {/if}
521 </div>516 </div>
522 </div>517 </div>
523 {/each}518 {/each}
@@ -527,7 +522,6 @@ $effect(() => {
527 {/if}522 {/if}
528 523
529 <style>524 <style>
530-
531 .chat-area, .users-list, input {525 .chat-area, .users-list, input {
532 font-size: 1rem;526 font-size: 1rem;
533 font-family: Arial, Verdana, Tahoma, sans-serif;527 font-family: Arial, Verdana, Tahoma, sans-serif;
@@ -608,14 +602,14 @@ $effect(() => {
608 color: #666;602 color: #666;
609 }603 }
610 604
611- .user .status-message {605+ /* .user .status-message {
612 font-size: 0.875rem;606 font-size: 0.875rem;
613 color: #666;607 color: #666;
614 font-style: italic;608 font-style: italic;
615 overflow: hidden;609 overflow: hidden;
616 text-overflow: ellipsis;610 text-overflow: ellipsis;
617 white-space: nowrap;611 white-space: nowrap;
618- }612+ } */
619 613
620 .sunken-panel {614 .sunken-panel {
621 background: white;615 background: white;
M src/lib/components/desktop-icons.svelte +3 -3
@@ -25,7 +25,7 @@
2525 role="button"
2626 tabindex="0"
2727 >
28- <img src="/desktop/aim-desktop-icon.png" alt="Chat Room" />
28+ <img src="/desktop/aim-desktop-icon.png" alt="Salon de discussion" />
2929 <span class="icon-label">Pdr Aim</span>
3030 </div>
3131
@@ -36,8 +36,8 @@
3636 role="button"
3737 tabindex="0"
3838 >
39- <img src="/desktop/application-x-shellscript.png" alt="Sign In" />
40- <span class="icon-label">login</span>
39+ <img src="/desktop/application-x-shellscript.png" alt="Connexion" />
40+ <span class="icon-label">Connexion</span>
4141 </div>
4242 </div>
4343
@@ -25,7 +25,7 @@
25 role="button" 25 role="button"
26 tabindex="0"26 tabindex="0"
27 >27 >
28- <img src="/desktop/aim-desktop-icon.png" alt="Chat Room" />28+ <img src="/desktop/aim-desktop-icon.png" alt="Salon de discussion" />
29 <span class="icon-label">Pdr Aim</span>29 <span class="icon-label">Pdr Aim</span>
30 </div>30 </div>
31 31
@@ -36,8 +36,8 @@
36 role="button" 36 role="button"
37 tabindex="0"37 tabindex="0"
38 >38 >
39- <img src="/desktop/application-x-shellscript.png" alt="Sign In" />39+ <img src="/desktop/application-x-shellscript.png" alt="Connexion" />
40- <span class="icon-label">login</span>40+ <span class="icon-label">Connexion</span>
41 </div>41 </div>
42 </div>42 </div>
43 43
M src/lib/components/ui/button-loading.svelte +2 -2
@@ -2,10 +2,10 @@
22 import { onDestroy } from 'svelte';
33
44 // Props with defaults:
5- // - text: button label when not loading (default "Send")
5+ // - text: button label when not loading (default "Envoyer")
66 // - loading: indicates if the button should show a spinner (default false)
77 // - spinnerChar: the character to use for the spinner animation (default '.')
8- let { onclick, disabled = $bindable(false), text, loading = $bindable(false), spinnerChar = $bindable('.') } = $props();
8+ let { onclick, disabled = $bindable(false), text = 'Envoyer', loading = $bindable(false), spinnerChar = $bindable('.') } = $props();
99
1010 // Using $state for internal reactive variables.
1111 let animatedDots = $state(''); // will hold the animated spinner text
@@ -2,10 +2,10 @@
2 import { onDestroy } from 'svelte';2 import { onDestroy } from 'svelte';
3 3
4 // Props with defaults:4 // Props with defaults:
5- // - text: button label when not loading (default "Send")5+ // - text: button label when not loading (default "Envoyer")
6 // - loading: indicates if the button should show a spinner (default false)6 // - loading: indicates if the button should show a spinner (default false)
7 // - spinnerChar: the character to use for the spinner animation (default '.')7 // - spinnerChar: the character to use for the spinner animation (default '.')
8- let { onclick, disabled = $bindable(false), text, loading = $bindable(false), spinnerChar = $bindable('.') } = $props();8+ let { onclick, disabled = $bindable(false), text = 'Envoyer', loading = $bindable(false), spinnerChar = $bindable('.') } = $props();
9 9
10 // Using $state for internal reactive variables.10 // Using $state for internal reactive variables.
11 let animatedDots = $state(''); // will hold the animated spinner text11 let animatedDots = $state(''); // will hold the animated spinner text
M src/lib/components/ui/loading-dots.svelte +1 -1
@@ -1,6 +1,6 @@
11 <!-- A reusable loading dots component with animated dots -->
22 <script lang="ts">
3- let { text = 'Loading', color = 'inherit' } = $props();
3+ let { text = 'Chargement', color = 'inherit' } = $props();
44 </script>
55
66 <div class="loading-text" style="color: {color}">
@@ -1,6 +1,6 @@
1 <!-- A reusable loading dots component with animated dots -->1 <!-- A reusable loading dots component with animated dots -->
2 <script lang="ts">2 <script lang="ts">
3- let { text = 'Loading', color = 'inherit' } = $props();3+ let { text = 'Chargement', color = 'inherit' } = $props();
4 </script>4 </script>
5 5
6 <div class="loading-text" style="color: {color}">6 <div class="loading-text" style="color: {color}">

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close