mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-02-20 07:37:26 -05:00
* added some files `standard` mime type * Used `TEXTViewer` Component to show Code Preview * Update Thumb.tsx * added `prismjs` * removed unnecessary comment * `CODEViewer` Component for Syntax Highlighting * formatting * using **Atom** Theme for `Prism` * merge text/code viewers & bg-app-focus for prism currently calling onError and onLoad without an Event argument that should change but i'm not really sure what to do there * removed unused imports * Update index.ts * `TEXTViewer` to `TextViewer_` * `TextViewer_` to `TextViewer` * Don't highlight normal TextFiles * clean code * `TEXTViewer` to `TextViewer` * using tailwind classes more * doing things correctly. * installed `prismjs` in interface * using own scroller * Update Thumb.tsx * Add an AbortController to the fetch request - Fix onError and onLoad calls - Format code * Fix onError being called when request was aborted due to re-render - Fix Compoenent re-rendering loop due to circular reference in useEffect - Remove unused imports * Improve text file serving and code syntax highlight - Implement way to identify text files in file-ext crate - Do not depend only on the file extension to identify text files in custom_uri - Import more prismjs language rules files - Add line numbers to TextViewer when rendering code * Clippy and prettier * Fix reading zero byte data to Vec - Improve empty file handling * Expand code highlight to more file types - Fix 10MB when it should be 10KB - Add supported for more code and config files extensions to sd-file-ext - Add comlink and vite-plugin-comlink for easy js worker integration - Move Prismjs logic to a Worker, because larger files (1000+ lines) where causing the UI to hang - Replace line-number prismjs plugin with our own implementation * Fix uppercase extension name --------- Co-authored-by: Utku <74243531+utkubakir@users.noreply.github.com> Co-authored-by: pr <pineapplerind.info@gmail.com> Co-authored-by: Vítor Vasconcellos <vasconcellos.dev@gmail.com>
102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
import clsx from 'clsx';
|
|
import { memo, useEffect, useRef, useState } from 'react';
|
|
import './prism.css';
|
|
|
|
export interface TextViewerProps {
|
|
src: string;
|
|
onLoad?: (event: HTMLElementEventMap['load']) => void;
|
|
onError?: (event: HTMLElementEventMap['error']) => void;
|
|
className?: string;
|
|
codeExtension?: string;
|
|
}
|
|
|
|
// prettier-ignore
|
|
type Worker = typeof import('./worker')
|
|
export const worker = new ComlinkWorker<Worker>(new URL('./worker', import.meta.url));
|
|
|
|
const NEW_LINE_EXP = /\n(?!$)/g;
|
|
|
|
export const TextViewer = memo(
|
|
({ src, onLoad, onError, className, codeExtension }: TextViewerProps) => {
|
|
const ref = useRef<HTMLPreElement>(null);
|
|
const [highlight, setHighlight] = useState<{
|
|
code: string;
|
|
length: number;
|
|
language: string;
|
|
}>();
|
|
const [textContent, setTextContent] = useState('');
|
|
|
|
useEffect(() => {
|
|
// Ignore empty urls
|
|
if (!src || src === '#') return;
|
|
|
|
const controller = new AbortController();
|
|
|
|
fetch(src, { mode: 'cors', signal: controller.signal })
|
|
.then(async (response) => {
|
|
if (!response.ok) throw new Error(`Invalid response: ${response.statusText}`);
|
|
const text = await response.text();
|
|
|
|
if (controller.signal.aborted) return;
|
|
|
|
onLoad?.(new UIEvent('load', {}));
|
|
setTextContent(text);
|
|
|
|
if (codeExtension) {
|
|
try {
|
|
const env = await worker.highlight(text, codeExtension);
|
|
if (env && !controller.signal.aborted) {
|
|
const match = text.match(NEW_LINE_EXP);
|
|
setHighlight({
|
|
...env,
|
|
length: (match ? match.length + 1 : 1) + 1
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
if (!controller.signal.aborted)
|
|
onError?.(new ErrorEvent('error', { message: `${error}` }));
|
|
});
|
|
|
|
return () => controller.abort();
|
|
}, [src, onError, onLoad, codeExtension]);
|
|
|
|
return (
|
|
<pre
|
|
ref={ref}
|
|
tabIndex={0}
|
|
className={clsx(
|
|
className,
|
|
highlight && ['relative !pl-[3.8em]', `language-${highlight.language}`]
|
|
)}
|
|
>
|
|
{highlight ? (
|
|
<>
|
|
<span className="pointer-events-none absolute left-0 top-[1em] w-[3em] select-none text-[100%] tracking-[-1px] text-ink-dull">
|
|
{Array.from(highlight, (_, i) => (
|
|
<span
|
|
key={i}
|
|
className={clsx('token block text-end', i % 2 && 'bg-black/40')}
|
|
>
|
|
{i + 1}
|
|
</span>
|
|
))}
|
|
</span>
|
|
<code
|
|
style={{ whiteSpace: 'inherit' }}
|
|
className={clsx('relative', `language-${highlight.language}`)}
|
|
dangerouslySetInnerHTML={{ __html: highlight.code }}
|
|
/>
|
|
</>
|
|
) : (
|
|
textContent
|
|
)}
|
|
</pre>
|
|
);
|
|
}
|
|
);
|