Skip to content

Commit

Permalink
🌟 feat: Enhance User Experience and SEO with Accessibility Updates an…
Browse files Browse the repository at this point in the history
…d robots.txt (danny-avila#5392)

* 🔈 fix: Refactor AudioRecorder to use button element for improved accessibility

* 🔈 fix: Update conversation menu button ID for improved accessibility

* 🔈 fix: Remove redundant role attribute from SidePanel for improved accessibility

* feat: Add robots.txt to manage web crawler access

* feat: Update index.html with meta description and remove legacy file

* fix: resolve merge conflicts.

* fix: resolve merge conflicts.

* fix: resolve merge conflicts.

* feat: Update index.html with meta description and remove legacy file

* 🔧 feat: Add legacy support and improve SidePanel accessibility

* 🔧 feat: Integrate express-static-gzip for improved static file serving and add new plugins for enhanced functionality

* 🔧 chore: Remove unused HTML ESLint plugin configurations and dependencies

---------

Co-authored-by: Ruben Talstra <[email protected]>
  • Loading branch information
berry-13 and rubentalstra authored Feb 22, 2025
1 parent 96c091c commit b404e37
Show file tree
Hide file tree
Showing 11 changed files with 1,723 additions and 310 deletions.
1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"express-mongo-sanitize": "^2.2.0",
"express-rate-limit": "^7.4.1",
"express-session": "^1.18.1",
"express-static-gzip": "^2.2.0",
"file-type": "^18.7.0",
"firebase": "^11.0.2",
"googleapis": "^126.0.1",
Expand Down
14 changes: 7 additions & 7 deletions api/server/utils/staticCache.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const express = require('express');
const expressStaticGzip = require('express-static-gzip');

const oneDayInSeconds = 24 * 60 * 60;

const sMaxAge = process.env.STATIC_CACHE_S_MAX_AGE || oneDayInSeconds;
const maxAge = process.env.STATIC_CACHE_MAX_AGE || oneDayInSeconds * 2;

const staticCache = (staticPath) =>
express.static(staticPath, {
setHeaders: (res) => {
if (process.env.NODE_ENV?.toLowerCase() !== 'production') {
return;
expressStaticGzip(staticPath, {
enableBrotli: false, // disable Brotli, only using gzip
orderPreference: ['gz'],
setHeaders: (res, _path) => {
if (process.env.NODE_ENV?.toLowerCase() === 'production') {
res.setHeader('Cache-Control', `public, max-age=${maxAge}, s-maxage=${sMaxAge}`);
}

res.setHeader('Cache-Control', `public, max-age=${maxAge}, s-maxage=${sMaxAge}`);
},
});

Expand Down
2 changes: 1 addition & 1 deletion client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="description" content="LibreChat - An open source chat application with support for multiple AI models" />
<title>LibreChat</title>
<link rel="shortcut icon" href="#" />
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png" />
Expand Down Expand Up @@ -53,6 +54,5 @@
<div id="root">
<div id="loading-container"></div>
</div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
"typescript": "^5.3.3",
"vite": "^6.1.0",
"vite-plugin-node-polyfills": "^0.17.0",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.21.1"
}
}
3 changes: 3 additions & 0 deletions client/public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
User-agent: *
Disallow: /api/
Allow: /
30 changes: 19 additions & 11 deletions client/src/components/Chat/Input/AudioRecorder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,25 @@ export default function AudioRecorder({

return (
<TooltipAnchor
id="audio-recorder"
aria-label={localize('com_ui_use_micrphone')}
onClick={isListening === true ? handleStopRecording : handleStartRecording}
disabled={disabled}
className={cn(
'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover',
isRTL ? 'bottom-2 left-2' : 'bottom-2 right-2',
)}
description={localize('com_ui_use_micrphone')}
>
{renderIcon()}
</TooltipAnchor>
render={
<button
id="audio-recorder"
type="button"
aria-label={localize('com_ui_use_micrphone')}
onClick={isListening === true ? handleStopRecording : handleStartRecording}
disabled={disabled}
className={cn(
'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover',
isRTL ? 'bottom-2 left-2' : 'bottom-2 right-2',
disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer',
)}
title={localize('com_ui_use_micrphone')}
aria-pressed={isListening}
>
{renderIcon()}
</button>
}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ function ConvoOptions({
setIsOpen={setIsPopoverActive}
trigger={
<Menu.MenuButton
id="conversation-menu-button"
id={`conversation-menu-${conversationId}`}
aria-label={localize('com_nav_convo_menu_options')}
className={cn(
'z-30 inline-flex h-7 w-7 items-center justify-center gap-2 rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/SidePanel/SidePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const SidePanel = ({
id="controls-nav"
order={hasArtifacts != null ? 3 : 2}
aria-label={localize('com_ui_controls')}
role="region"
role="navigation"
collapsedSize={collapsedSize}
defaultSize={defaultSize}
collapsible={true}
Expand Down
106 changes: 60 additions & 46 deletions client/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,14 @@
import path, { resolve } from 'path';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
import { defineConfig, createLogger } from 'vite';
import { defineConfig } from 'vite';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
import compression from 'vite-plugin-compression';
import type { Plugin } from 'vite';

const logger = createLogger();
const originalWarning = logger.warn;
logger.warn = (msg, options) => {
/* Suppresses:
[vite:css] Complex selectors in '.group:focus-within .dark\:group-focus-within\:text-gray-300:is(.dark *)' can not be transformed to an equivalent selector without ':is()'.
*/
if (msg.includes('vite:css') && msg.includes('^^^^^^^')) {
return;
}
/* Suppresses:
(!) Some chunks are larger than 500 kB after minification. Consider:
- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
*/
if (msg.includes('Use build.rollupOptions.output.manualChunks')) {
return;
}
originalWarning(msg, options);
};

// https://vitejs.dev/config/
export default defineConfig({
customLogger: logger,
server: {
fs: {
cachedChecks: false,
},
host: 'localhost',
port: 3090,
strictPort: false,
Expand All @@ -47,7 +23,7 @@ export default defineConfig({
},
},
},
// All other env variables are filtered out
// Set the directory where environment variables are loaded from and restrict prefixes
envDir: '../',
envPrefix: ['VITE_', 'SCRIPT_', 'DOMAIN_', 'ALLOW_'],
plugins: [
Expand All @@ -57,11 +33,14 @@ export default defineConfig({
injectRegister: 'auto', // 'auto' | 'manual' | 'disabled'
registerType: 'autoUpdate', // 'prompt' | 'autoUpdate'
devOptions: {
enabled: false, // enable/disable registering SW in development mode
enabled: false, // disable service worker registration in development mode
},
useCredentials: true,
workbox: {
globPatterns: ['assets/**/*.{png,jpg,svg,ico}', '**/*.{js,css,html,ico,woff2}'],
globPatterns: [
'assets/**/*.{png,jpg,svg,ico}',
'**/*.{js,css,html,ico,woff2}',
],
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
navigateFallbackDenylist: [/^\/oauth/],
},
Expand Down Expand Up @@ -103,33 +82,70 @@ export default defineConfig({
},
}),
sourcemapExclude({ excludeNodeModules: true }),
compression({
verbose: true,
disable: false,
threshold: 10240, // compress files larger than 10KB
algorithm: 'gzip',
ext: '.gz',
}),
],
publicDir: './public',
build: {
sourcemap: process.env.NODE_ENV === 'development',
outDir: './dist',
minify: 'terser',
rollupOptions: {
preserveEntrySignatures: 'strict',
// external: ['uuid'],
output: {
manualChunks: (id) => {
if (id.includes('node_modules/highlight.js')) {
return 'markdown_highlight';
}
if (id.includes('node_modules/hast-util-raw')) {
return 'markdown_large';
}
if (id.includes('node_modules/katex')) {
return 'markdown_large';
}
manualChunks(id: string) {
if (id.includes('node_modules')) {
// Group Radix UI libraries together.
if (id.includes('@radix-ui')) {
return 'radix-ui';
}
// Group framer-motion separately.
if (id.includes('framer-motion')) {
return 'framer-motion';
}
// Group markdown-related libraries.
if (id.includes('node_modules/highlight.js')) {
return 'markdown_highlight';
}
if (
id.includes('node_modules/hast-util-raw') ||
id.includes('node_modules/katex')
) {
return 'markdown_large';
}
// Group TanStack libraries together.
if (id.includes('@tanstack')) {
return 'tanstack-vendor';
}
// Additional grouping for other node_modules:
if (id.includes('@headlessui')) {
return 'headlessui';
}

// Everything else falls into a generic vendor chunk.
return 'vendor';
}
// Create a separate chunk for all locale files under src/locales.
if (id.includes(path.join('src', 'locales'))) {
return 'locales';
}
// Let Rollup decide automatically for any other files.
return null;
},
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: (assetInfo) => {
if (assetInfo.name && /\.(woff|woff2|eot|ttf|otf)$/.test(assetInfo.name)) {
return 'assets/[name][extname]';
if (
assetInfo.names &&
/\.(woff|woff2|eot|ttf|otf)$/.test(assetInfo.names)
) {
return 'assets/fonts/[name][extname]';
}
return 'assets/[name].[hash][extname]';
},
Expand All @@ -139,15 +155,13 @@ export default defineConfig({
* @see {@link https://github.com/TanStack/query/pull/5161#issuecomment-1477389761 Preserve 'use client' directives TanStack/query#5161}
*/
onwarn(warning, warn) {
if (
// warning.code === 'MODULE_LEVEL_DIRECTIVE' &&
warning.message.includes('Error when using sourcemap')
) {
if (warning.message.includes('Error when using sourcemap')) {
return;
}
warn(warning);
},
},
chunkSizeWarningLimit: 1200,
},
resolve: {
alias: {
Expand All @@ -173,4 +187,4 @@ export function sourcemapExclude(opts?: SourcemapExclude): Plugin {
}
},
};
}
}
49 changes: 0 additions & 49 deletions index.html

This file was deleted.

Loading

0 comments on commit b404e37

Please sign in to comment.