♻️(front) optimize syntax highlighting bundle size
Replace rehype-pretty-code with direct Shiki integration Migrate syntax highlighting from rehype-pretty-code to @shikijs/rehype for better bundle optimization. Create a centralized Shiki highlighter that pre-loads only required languages. Also remove unused dependencies: @ag-media/react-pdf-table, @react-pdf/renderer, react-select. Signed-off-by: Laurent Paoletti <lp@providenz.fr>
This commit is contained in:
committed by
Laurent Paoletti
parent
ff9e8335f2
commit
23964cbf12
@@ -18,6 +18,7 @@ and this project adheres to
|
||||
### Changed
|
||||
|
||||
- 🏗️(back) migrate to uv
|
||||
- ♻️(front) optimize syntax highlighting bundle size
|
||||
|
||||
## [0.0.12] - 2026-01-27
|
||||
|
||||
@@ -45,6 +46,7 @@ and this project adheres to
|
||||
- ⚰️(back) remove dead code and unused files
|
||||
- 🐛(back) prevent tool call timeouts
|
||||
|
||||
|
||||
### Removed
|
||||
|
||||
- 🔥(chat) remove thinking part from frontend #227
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-media/react-pdf-table": "2.0.3",
|
||||
"@ai-sdk/react": "1.2.12",
|
||||
"@ai-sdk/ui-utils": "1.2.11",
|
||||
"@emoji-mart/data": "1.2.1",
|
||||
@@ -26,8 +25,8 @@
|
||||
"@gouvfr-lasuite/integration": "1.0.3",
|
||||
"@gouvfr-lasuite/ui-kit": "0.18.4",
|
||||
"@openfun/cunningham-react": "4.0.0",
|
||||
"@react-pdf/renderer": "4.3.0",
|
||||
"@sentry/nextjs": "9.26.0",
|
||||
"@shikijs/rehype": "^3.21.0",
|
||||
"@tanstack/react-query": "5.80.5",
|
||||
"canvg": "4.0.3",
|
||||
"clsx": "2.1.1",
|
||||
@@ -37,7 +36,7 @@
|
||||
"i18next": "25.2.1",
|
||||
"i18next-browser-languagedetector": "8.1.0",
|
||||
"idb": "8.0.3",
|
||||
"lodash": "4.17.21",
|
||||
"lodash": "4.17.23",
|
||||
"lottie-react": "^2.4.1",
|
||||
"luxon": "3.6.1",
|
||||
"micromark-extension-llm-math": "3.1.1-20250610",
|
||||
@@ -49,12 +48,10 @@
|
||||
"react-i18next": "15.5.2",
|
||||
"react-intersection-observer": "9.16.0",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-select": "5.10.1",
|
||||
"rehype-katex": "7.0.1",
|
||||
"rehype-pretty-code": "^0.14.1",
|
||||
"remark-gfm": "4.0.1",
|
||||
"remark-math": "6.0.0",
|
||||
"shiki": "^3.13.0",
|
||||
"shiki": "^3.21.0",
|
||||
"styled-components": "6.1.18",
|
||||
"use-debounce": "10.0.4",
|
||||
"zod": "^3.25.67",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Message, SourceUIPart, ToolInvocationUIPart } from '@ai-sdk/ui-utils';
|
||||
import { Modal, ModalSize } from '@openfun/cunningham-react';
|
||||
import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
|
||||
import 'katex/dist/katex.min.css'; // `rehype-katex` does not import the CSS for you
|
||||
import { useRouter } from 'next/router';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { use, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { ChangeEvent, FormEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MarkdownHooks } from 'react-markdown';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypePrettyCode from 'rehype-pretty-code';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
|
||||
@@ -35,6 +35,9 @@ import { useSourceMetadataCache } from '../hooks';
|
||||
import { useChatPreferencesStore } from '../stores/useChatPreferencesStore';
|
||||
import { usePendingChatStore } from '../stores/usePendingChatStore';
|
||||
import { useScrollStore } from '../stores/useScrollStore';
|
||||
import { getHighlighter } from '../utils/shiki';
|
||||
|
||||
const highlighterPromise = getHighlighter();
|
||||
|
||||
// Define Attachment type locally (mirroring backend structure)
|
||||
export interface Attachment {
|
||||
@@ -51,7 +54,7 @@ export const Chat = ({
|
||||
const { t } = useTranslation();
|
||||
const copyToClipboard = useClipboard();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
|
||||
const highlighter = use(highlighterPromise);
|
||||
const streamProtocol = 'data'; // or 'text'
|
||||
|
||||
const {
|
||||
@@ -727,9 +730,11 @@ export const Chat = ({
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[
|
||||
[
|
||||
rehypePrettyCode,
|
||||
rehypeShikiFromHighlighter,
|
||||
highlighter,
|
||||
{
|
||||
theme: 'github-dark-dimmed',
|
||||
fallbackLanguage: 'plaintext',
|
||||
},
|
||||
],
|
||||
rehypeKatex,
|
||||
|
||||
@@ -71,10 +71,12 @@ export const CodeBlock = ({ children, ...props }: CodeBlockProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CopyCodeButton onCopy={handleCopy} />
|
||||
<Box ref={preRef} $position="relative" as="pre" {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
<figure data-rehype-pretty-code-figure="">
|
||||
<CopyCodeButton onCopy={handleCopy} />
|
||||
<Box ref={preRef} $position="relative" as="pre" {...props}>
|
||||
{children}
|
||||
</Box>
|
||||
</figure>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { createHighlighterCore } from 'shiki/core';
|
||||
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
|
||||
import langBash from 'shiki/langs/bash.mjs';
|
||||
import langCss from 'shiki/langs/css.mjs';
|
||||
import langHtml from 'shiki/langs/html.mjs';
|
||||
import langJavascript from 'shiki/langs/javascript.mjs';
|
||||
import langJson from 'shiki/langs/json.mjs';
|
||||
import langMarkdown from 'shiki/langs/markdown.mjs';
|
||||
import langPython from 'shiki/langs/python.mjs';
|
||||
import langSql from 'shiki/langs/sql.mjs';
|
||||
import langTypescript from 'shiki/langs/typescript.mjs';
|
||||
import langYaml from 'shiki/langs/yaml.mjs';
|
||||
import themeDimmed from 'shiki/themes/github-dark-dimmed.mjs';
|
||||
|
||||
export const getHighlighter = () =>
|
||||
createHighlighterCore({
|
||||
themes: [themeDimmed],
|
||||
langs: [
|
||||
langJavascript,
|
||||
langTypescript,
|
||||
langPython,
|
||||
langBash,
|
||||
langJson,
|
||||
langHtml,
|
||||
langCss,
|
||||
langSql,
|
||||
langYaml,
|
||||
langMarkdown,
|
||||
],
|
||||
|
||||
engine: createOnigurumaEngine(import('shiki/wasm')),
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import type { ReactElement } from 'react';
|
||||
import { type ReactElement, Suspense } from 'react';
|
||||
|
||||
import { Loader } from '@/components';
|
||||
import { Chat } from '@/features/chat/components/Chat';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
@@ -9,7 +10,11 @@ const Page: NextPageWithLayout = () => {
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
|
||||
return <Chat initialConversationId={id as string} />;
|
||||
return (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Chat initialConversationId={id as string} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { type ReactElement, Suspense } from 'react';
|
||||
|
||||
import { Loader } from '@/components';
|
||||
import { Chat } from '@/features/chat/components/Chat';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
return <Chat initialConversationId={undefined} />;
|
||||
return (
|
||||
<Suspense fallback={<Loader />}>
|
||||
<Chat initialConversationId={undefined} />
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
|
||||
+78
-629
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user