mirror of
https://github.com/diced/zipline.git
synced 2025-12-05 20:40:12 -08:00
fix: performance issues with code renderer (#911)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
"react-dom": "^19.1.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-router-dom": "^7.8.2",
|
||||
"react-window": "1.8.11",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sharp": "^0.34.3",
|
||||
"swr": "^2.3.6",
|
||||
@@ -96,6 +97,7 @@
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@vitejs/plugin-react": "^5.0.2",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@@ -179,6 +179,9 @@ importers:
|
||||
react-router-dom:
|
||||
specifier: ^7.8.2
|
||||
version: 7.8.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
react-window:
|
||||
specifier: 1.8.11
|
||||
version: 1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
|
||||
remark-gfm:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
@@ -228,6 +231,9 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ^19.1.9
|
||||
version: 19.1.9(@types/react@19.1.12)
|
||||
'@types/react-window':
|
||||
specifier: ^1.8.8
|
||||
version: 1.8.8
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2(vite@7.1.4(@types/node@24.3.0)(jiti@2.5.1)(sass@1.92.0)(sugarss@5.0.1(postcss@8.5.6))(tsx@4.20.5))
|
||||
@@ -2058,6 +2064,9 @@ packages:
|
||||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
'@types/react-window@1.8.8':
|
||||
resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==}
|
||||
|
||||
'@types/react@19.1.12':
|
||||
resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==}
|
||||
|
||||
@@ -3552,6 +3561,9 @@ packages:
|
||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
|
||||
merge2@1.4.1:
|
||||
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -4183,6 +4195,13 @@ packages:
|
||||
react: '>=16.6.0'
|
||||
react-dom: '>=16.6.0'
|
||||
|
||||
react-window@1.8.11:
|
||||
resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==}
|
||||
engines: {node: '>8.0.0'}
|
||||
peerDependencies:
|
||||
react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react@19.1.1:
|
||||
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -7150,6 +7169,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/react': 19.1.12
|
||||
|
||||
'@types/react-window@1.8.8':
|
||||
dependencies:
|
||||
'@types/react': 19.1.12
|
||||
|
||||
'@types/react@19.1.12':
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
@@ -8961,6 +8984,8 @@ snapshots:
|
||||
|
||||
media-typer@0.3.0: {}
|
||||
|
||||
memoize-one@5.2.1: {}
|
||||
|
||||
merge2@1.4.1: {}
|
||||
|
||||
micromark-core-commonmark@2.0.3:
|
||||
@@ -9693,6 +9718,13 @@ snapshots:
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
react-window@1.8.11(react-dom@19.1.1(react@19.1.1))(react@19.1.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.3
|
||||
memoize-one: 5.2.1
|
||||
react: 19.1.1
|
||||
react-dom: 19.1.1(react@19.1.1)
|
||||
|
||||
react@19.1.1: {}
|
||||
|
||||
read-package-up@11.0.0:
|
||||
|
||||
@@ -234,15 +234,15 @@ export default function FileModal({
|
||||
</Title>
|
||||
<Combobox
|
||||
zIndex={90000}
|
||||
withinPortal={false}
|
||||
store={tagsCombobox}
|
||||
onOptionSubmit={handleValueSelect}
|
||||
withinPortal={false}
|
||||
>
|
||||
<Combobox.DropdownTarget>
|
||||
<PillsInput
|
||||
onBlur={() => triggerSave()}
|
||||
pointer
|
||||
onClick={() => tagsCombobox.toggleDropdown()}
|
||||
onClick={() => tagsCombobox.openDropdown()}
|
||||
>
|
||||
<Pill.Group>
|
||||
{values.length > 0 ? (
|
||||
@@ -254,9 +254,14 @@ export default function FileModal({
|
||||
<Combobox.EventsTarget>
|
||||
<PillsInput.Field
|
||||
type='hidden'
|
||||
onFocus={() => tagsCombobox.openDropdown()}
|
||||
onBlur={() => tagsCombobox.closeDropdown()}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Backspace') {
|
||||
if (
|
||||
event.key === 'Backspace' &&
|
||||
value.length > 0 &&
|
||||
event.currentTarget.value === ''
|
||||
) {
|
||||
event.preventDefault();
|
||||
handleValueRemove(value[value.length - 1]);
|
||||
}
|
||||
@@ -285,9 +290,7 @@ export default function FileModal({
|
||||
</Combobox.Option>
|
||||
))
|
||||
) : (
|
||||
<Combobox.Option value='no-tags' disabled>
|
||||
No tags found, create one outside of this menu.
|
||||
</Combobox.Option>
|
||||
<Combobox.Empty>No tags found, create one outside of this menu.</Combobox.Empty>
|
||||
)}
|
||||
</Combobox.Options>
|
||||
</Combobox.Dropdown>
|
||||
@@ -310,8 +313,8 @@ export default function FileModal({
|
||||
</Button>
|
||||
) : (
|
||||
<Combobox
|
||||
store={folderCombobox}
|
||||
withinPortal={false}
|
||||
store={folderCombobox}
|
||||
onOptionSubmit={(value) => handleAdd(value)}
|
||||
>
|
||||
<Combobox.Target>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
.theme {
|
||||
color: var(--_color);
|
||||
background: var(--_background);
|
||||
display: block;
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ActionIcon, Button, CopyButton, Paper, ScrollArea, Text, useMantineTheme } from '@mantine/core';
|
||||
import { IconCheck, IconClipboardCopy, IconChevronDown, IconChevronUp } from '@tabler/icons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IconCheck, IconChevronDown, IconChevronUp, IconClipboardCopy } from '@tabler/icons-react';
|
||||
import type { HLJSApi } from 'highlight.js';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { FixedSizeList as List } from 'react-window';
|
||||
|
||||
import './HighlightCode.theme.scss';
|
||||
import { type HLJSApi } from 'highlight.js';
|
||||
|
||||
export default function HighlightCode({ language, code }: { language: string; code: string }) {
|
||||
const theme = useMantineTheme();
|
||||
@@ -14,15 +15,56 @@ export default function HighlightCode({ language, code }: { language: string; co
|
||||
import('highlight.js').then((mod) => setHljs(mod.default || mod));
|
||||
}, []);
|
||||
|
||||
const lines = code.split('\n');
|
||||
const lineNumbers = lines.map((_, i) => i + 1);
|
||||
const displayLines = expanded ? lines : lines.slice(0, 50);
|
||||
const displayLineNumbers = expanded ? lineNumbers : lineNumbers.slice(0, 50);
|
||||
const lines = useMemo(() => code.split('\n'), [code]);
|
||||
const visible = expanded ? lines.length : Math.min(lines.length, 50);
|
||||
const expandable = lines.length > 50;
|
||||
|
||||
let lang = language;
|
||||
if (!hljs || !hljs.getLanguage(lang)) {
|
||||
lang = 'text';
|
||||
}
|
||||
const lang = useMemo(() => {
|
||||
if (!hljs) return 'plaintext';
|
||||
if (hljs.getLanguage(language)) return language;
|
||||
|
||||
return 'plaintext';
|
||||
}, [hljs, language]);
|
||||
|
||||
const hlLines = useMemo(() => {
|
||||
if (!hljs) return lines;
|
||||
|
||||
return lines.map(
|
||||
(line) =>
|
||||
hljs.highlight(line, {
|
||||
language: lang,
|
||||
}).value,
|
||||
);
|
||||
}, [lines, hljs, lang]);
|
||||
|
||||
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
whiteSpace: 'pre',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.8rem',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
component='span'
|
||||
c='dimmed'
|
||||
mr='md'
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
width: 40,
|
||||
textAlign: 'right',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{index + 1}
|
||||
</Text>
|
||||
|
||||
<code className='theme hljs' style={{ flex: 1 }} dangerouslySetInnerHTML={{ __html: hlLines[index] }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper withBorder p='xs' my='md' pos='relative'>
|
||||
@@ -44,37 +86,17 @@ export default function HighlightCode({ language, code }: { language: string; co
|
||||
)}
|
||||
</CopyButton>
|
||||
|
||||
<ScrollArea type='auto' dir='ltr' offsetScrollbars={false}>
|
||||
<pre style={{ margin: 0, whiteSpace: 'pre', overflowX: 'auto' }} className='theme'>
|
||||
<code className='theme'>
|
||||
{displayLines.map((line, i) => (
|
||||
<div key={i}>
|
||||
<Text
|
||||
component='span'
|
||||
size='sm'
|
||||
c='dimmed'
|
||||
mr='md'
|
||||
style={{ userSelect: 'none', fontFamily: 'monospace' }}
|
||||
>
|
||||
{displayLineNumbers[i]}
|
||||
</Text>
|
||||
<span
|
||||
className='line'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: lang === 'none' || !hljs ? line : hljs.highlight(line, { language: lang }).value,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</pre>
|
||||
<ScrollArea type='auto' offsetScrollbars={false} style={{ maxHeight: 400 }}>
|
||||
<List height={400} width='100%' itemCount={visible} itemSize={20} overscanCount={10}>
|
||||
{Row}
|
||||
</List>
|
||||
</ScrollArea>
|
||||
|
||||
{lines.length > 50 && (
|
||||
{expandable && (
|
||||
<Button
|
||||
variant='outline'
|
||||
variant='light'
|
||||
size='compact-sm'
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
onClick={() => setExpanded((e) => !e)}
|
||||
leftSection={expanded ? <IconChevronUp size='1rem' /> : <IconChevronDown size='1rem' />}
|
||||
style={{ position: 'absolute', bottom: '0.5rem', right: '0.5rem' }}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user