Compare commits
4 Commits
feat/overl
...
feat/lang-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddc2030a42 | ||
|
|
776a1c5def | ||
|
|
5ceca3068e | ||
|
|
b489e1c1c1 |
37
package.json
37
package.json
@@ -3,34 +3,33 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"copy-to-clipboard": "^3.3.1",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"history": "^5.0.0",
|
"history": "^5.3.0",
|
||||||
"local-storage": "^2.0.0",
|
"local-storage": "^2.0.0",
|
||||||
"monaco-themes": "^0.4.4",
|
"monaco-themes": "^0.4.4",
|
||||||
"pako": "^2.0.3",
|
"pako": "^2.1.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-device-detect": "^2.1.2",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "^5.0.1",
|
"react-scripts": "^5.0.1",
|
||||||
"styled-components": "^5.2.1",
|
"styled-components": "^6.1.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.4.2",
|
||||||
"whatwg-mimetype": "^3.0.0"
|
"whatwg-mimetype": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.0.0",
|
||||||
"@testing-library/user-event": "^14.2.0",
|
"@testing-library/user-event": "^13.2.1",
|
||||||
"@types/jest": "^29.2.2",
|
"@types/jest": "^27.0.1",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"@types/pako": "^2.0.0",
|
"@types/pako": "^2.0.3",
|
||||||
"@types/react": "^18.0.25",
|
"@types/react": "^18.2.0",
|
||||||
"@types/react-dom": "^18.0.8",
|
"@types/react-dom": "^18.2.0",
|
||||||
"@types/styled-components": "^5.1.26",
|
"@types/whatwg-mimetype": "^3.0.2",
|
||||||
"@types/whatwg-mimetype": "^3.0.0",
|
"monaco-editor": "^0.44.0",
|
||||||
"monaco-editor": "^0.34.1",
|
"prettier": "^3.1.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier-plugin-organize-imports": "^3.2.4"
|
||||||
"prettier-plugin-organize-imports": "^3.1.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
|||||||
12
src/App.tsx
12
src/App.tsx
@@ -1,6 +1,8 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import Editor from './components/Editor';
|
import Editor from './components/Editor';
|
||||||
import { loadFromBytebin } from './util/storage';
|
import { loadFromBytebin } from './util/storage';
|
||||||
|
import { Language } from './util/language';
|
||||||
|
import { detectLanguage } from './util/detect-language';
|
||||||
|
|
||||||
const INITIAL = Symbol();
|
const INITIAL = Symbol();
|
||||||
const LOADING = Symbol();
|
const LOADING = Symbol();
|
||||||
@@ -13,7 +15,7 @@ export default function App() {
|
|||||||
const [state, setState] = useState<LoadingState>(INITIAL);
|
const [state, setState] = useState<LoadingState>(INITIAL);
|
||||||
const [forcedContent, setForcedContent] = useState<string>('');
|
const [forcedContent, setForcedContent] = useState<string>('');
|
||||||
const [actualContent, setActualContent] = useState<string>('');
|
const [actualContent, setActualContent] = useState<string>('');
|
||||||
const [contentType, setContentType] = useState<string>();
|
const [contentType, setContentType] = useState<Language>();
|
||||||
|
|
||||||
function setContent(content: string) {
|
function setContent(content: string) {
|
||||||
setActualContent(content);
|
setActualContent(content);
|
||||||
@@ -28,8 +30,14 @@ export default function App() {
|
|||||||
loadFromBytebin(pasteId).then(({ ok, content, type }) => {
|
loadFromBytebin(pasteId).then(({ ok, content, type }) => {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
setContent(content);
|
setContent(content);
|
||||||
if (type) {
|
if (type !== 'plain') {
|
||||||
setContentType(type);
|
setContentType(type);
|
||||||
|
} else {
|
||||||
|
detectLanguage(pasteId).then(detectedLanguage => {
|
||||||
|
if (detectedLanguage) {
|
||||||
|
setContentType(detectedLanguage);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setContent(get404Message(pasteId));
|
setContent(get404Message(pasteId));
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const Button = styled.div`
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
:hover {
|
&:hover {
|
||||||
background: ${props => props.theme.highlight};
|
background: ${props => props.theme.highlight};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import usePreference from '../hooks/usePreference';
|
|||||||
import themes, { Themes } from '../style/themes';
|
import themes, { Themes } from '../style/themes';
|
||||||
import EditorControls from './EditorControls';
|
import EditorControls from './EditorControls';
|
||||||
import EditorGlobalStyle from './EditorGlobalStyle';
|
import EditorGlobalStyle from './EditorGlobalStyle';
|
||||||
import EditorOverlay from './EditorOverlay';
|
|
||||||
import EditorTextArea from './EditorTextArea';
|
import EditorTextArea from './EditorTextArea';
|
||||||
|
import { Language } from '../util/language';
|
||||||
|
|
||||||
export interface EditorProps {
|
export interface EditorProps {
|
||||||
forcedContent: string;
|
forcedContent: string;
|
||||||
actualContent: string;
|
actualContent: string;
|
||||||
setActualContent: (value: string) => void;
|
setActualContent: (value: string) => void;
|
||||||
contentType?: string;
|
contentType?: Language;
|
||||||
pasteId?: string;
|
pasteId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,6 @@ export default function Editor({
|
|||||||
<>
|
<>
|
||||||
<ThemeProvider theme={themes[theme]}>
|
<ThemeProvider theme={themes[theme]}>
|
||||||
<EditorGlobalStyle />
|
<EditorGlobalStyle />
|
||||||
<EditorOverlay />
|
|
||||||
<EditorControls
|
<EditorControls
|
||||||
actualContent={actualContent}
|
actualContent={actualContent}
|
||||||
resetFunction={resetFunction}
|
resetFunction={resetFunction}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { MutableRefObject, useCallback, useEffect, useState } from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import themes, { Themes } from '../style/themes';
|
import themes, { Themes } from '../style/themes';
|
||||||
import { languages } from '../util/highlighting';
|
import { languages, unknownLanguage } from '../util/language';
|
||||||
import { saveToBytebin } from '../util/storage';
|
import { saveToBytebin } from '../util/storage';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import { ResetFunction } from './Editor';
|
import { ResetFunction } from './Editor';
|
||||||
@@ -104,9 +104,9 @@ export default function EditorControls({
|
|||||||
</Button>
|
</Button>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
label="language"
|
label="language"
|
||||||
value={language}
|
value={language === unknownLanguage ? '?' : language}
|
||||||
setValue={setLanguage}
|
setValue={setLanguage}
|
||||||
ids={languages}
|
ids={languages as unknown as Record<string, string[]>}
|
||||||
/>
|
/>
|
||||||
{readOnly && <Button onClick={unsetReadOnly}>[edit]</Button>}
|
{readOnly && <Button onClick={unsetReadOnly}>[edit]</Button>}
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { createGlobalStyle, ThemeProps } from 'styled-components';
|
import { createGlobalStyle } from 'styled-components';
|
||||||
import { Theme } from '../style/themes';
|
|
||||||
|
|
||||||
const EditorGlobalStyle = createGlobalStyle<ThemeProps<Theme>>`
|
const EditorGlobalStyle = createGlobalStyle`
|
||||||
html, body {
|
html, body {
|
||||||
color-scheme: ${props => props.theme.lightOrDark};
|
color-scheme: ${props => props.theme.lightOrDark};
|
||||||
scrollbar-color: ${props => props.theme.lightOrDark};
|
scrollbar-color: ${props => props.theme.lightOrDark};
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export default function EditorOverlay() {
|
|
||||||
return (
|
|
||||||
<EditorIntroOverlay>
|
|
||||||
<h1>{pastes}</h1>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<span># Upload a file</span>
|
|
||||||
</li>
|
|
||||||
<li>$ curl -T example.txt https://pastes.dev</li>
|
|
||||||
<br />
|
|
||||||
<li>
|
|
||||||
<span># Pipe output from another command</span>
|
|
||||||
</li>
|
|
||||||
<li>$ tail -n 50 file.log | curl -T - https://pastes.dev</li>
|
|
||||||
<br />
|
|
||||||
<li>
|
|
||||||
<span># Upload a file (without curl)</span>
|
|
||||||
</li>
|
|
||||||
<li>$ cat example.txt | netcat nc.pastes.dev 1337</li>
|
|
||||||
</ul>
|
|
||||||
</EditorIntroOverlay>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const EditorIntroOverlay = styled.div`
|
|
||||||
width: max(50vw, 400px);
|
|
||||||
height: 450px;
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
color: ${props => props.theme.primary};
|
|
||||||
text-align: center;
|
|
||||||
font-size: 5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
color: ${props => props.theme.comment};
|
|
||||||
}
|
|
||||||
|
|
||||||
color: ${props => props.theme.primary};
|
|
||||||
border: 5px solid ${props => props.theme.secondary};
|
|
||||||
background: ${props => props.theme.background};
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
`;
|
|
||||||
@@ -17,6 +17,7 @@ import themes, { Theme } from '../style/themes';
|
|||||||
|
|
||||||
import type { editor } from 'monaco-editor';
|
import type { editor } from 'monaco-editor';
|
||||||
import { ResetFunction } from './Editor';
|
import { ResetFunction } from './Editor';
|
||||||
|
import { logLanguage } from '../util/log-language';
|
||||||
|
|
||||||
export interface EditorTextAreaProps {
|
export interface EditorTextAreaProps {
|
||||||
forcedContent: string;
|
forcedContent: string;
|
||||||
@@ -60,6 +61,9 @@ export default function EditorTextArea({
|
|||||||
monaco.editor.defineTheme(theme.id, theme.editor);
|
monaco.editor.defineTheme(theme.id, theme.editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monaco.languages.register({ id: 'log' });
|
||||||
|
monaco.languages.setMonarchTokensProvider('log', logLanguage);
|
||||||
|
|
||||||
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
|
||||||
noSemanticValidation: true,
|
noSemanticValidation: true,
|
||||||
noSyntaxValidation: true,
|
noSyntaxValidation: true,
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ const Menu = styled.ul`
|
|||||||
}
|
}
|
||||||
|
|
||||||
> li.selected {
|
> li.selected {
|
||||||
::before {
|
&::before {
|
||||||
content: '*';
|
content: '*';
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import './style/base.css';
|
import './style/base.css';
|
||||||
|
import type {} from './style/styled';
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById('root') as HTMLElement
|
||||||
|
|||||||
6
src/style/styled.d.ts
vendored
Normal file
6
src/style/styled.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import 'styled-components';
|
||||||
|
import { Theme } from './themes';
|
||||||
|
|
||||||
|
declare module 'styled-components' {
|
||||||
|
export interface DefaultTheme extends Theme {}
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ export interface Theme {
|
|||||||
secondary: Color;
|
secondary: Color;
|
||||||
highlight: Color;
|
highlight: Color;
|
||||||
background: Color;
|
background: Color;
|
||||||
comment: Color;
|
|
||||||
lightOrDark: 'light' | 'dark';
|
lightOrDark: 'light' | 'dark';
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: Color;
|
color: Color;
|
||||||
@@ -38,7 +37,6 @@ const themes: Themes = {
|
|||||||
secondary: '#010409', // canvas.inset
|
secondary: '#010409', // canvas.inset
|
||||||
highlight: '#161b22', // canvas.overlay
|
highlight: '#161b22', // canvas.overlay
|
||||||
background: '#0d1117', // canvas.default
|
background: '#0d1117', // canvas.default
|
||||||
comment: '#8b949e',
|
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
|
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
@@ -61,6 +59,11 @@ const themes: Themes = {
|
|||||||
keyword: '#ff7b72',
|
keyword: '#ff7b72',
|
||||||
type: '#ffa657',
|
type: '#ffa657',
|
||||||
variable: '#ffa657',
|
variable: '#ffa657',
|
||||||
|
logInfo: '#3fb950', // green.3
|
||||||
|
logError: '#f85149', // red.4
|
||||||
|
logWarning: '#d29922', // yellow.3
|
||||||
|
logDate: '#33B3AE', // teal.3
|
||||||
|
logException: '#f8e3a1', // yellow.0
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -70,7 +73,6 @@ const themes: Themes = {
|
|||||||
secondary: '#022550',
|
secondary: '#022550',
|
||||||
highlight: '#36368c',
|
highlight: '#36368c',
|
||||||
background: '#ffffff',
|
background: '#ffffff',
|
||||||
comment: '#708090',
|
|
||||||
lightOrDark: 'light',
|
lightOrDark: 'light',
|
||||||
|
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
@@ -93,6 +95,11 @@ const themes: Themes = {
|
|||||||
keyword: '#0077aa',
|
keyword: '#0077aa',
|
||||||
type: '#DD4A68',
|
type: '#DD4A68',
|
||||||
variable: '#ee9900',
|
variable: '#ee9900',
|
||||||
|
logInfo: '#2da44e', // green.4
|
||||||
|
logError: '#cf222e', // red.5
|
||||||
|
logWarning: '#d4a72c', // yellow.3
|
||||||
|
logDate: '#136061', // teal.6
|
||||||
|
logException: '#7d4e00', // yellow.6
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -102,13 +109,18 @@ const themes: Themes = {
|
|||||||
secondary: '#383a59',
|
secondary: '#383a59',
|
||||||
highlight: '#44475a',
|
highlight: '#44475a',
|
||||||
background: '#282a36',
|
background: '#282a36',
|
||||||
comment: '#6272a4',
|
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#586e75',
|
color: '#586e75',
|
||||||
backgroundColor: '#44475a',
|
backgroundColor: '#44475a',
|
||||||
},
|
},
|
||||||
editor: dracula as editor.IStandaloneThemeData,
|
editor: addLogColors(dracula as editor.IStandaloneThemeData, {
|
||||||
|
info: '#50FA7B', // green
|
||||||
|
error: '#FF5555', // red
|
||||||
|
warning: '#FFB86C', // orange
|
||||||
|
date: '#BD93F9', // purple
|
||||||
|
exception: '#F1FA8C', // yellow
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
'monokai': {
|
'monokai': {
|
||||||
id: 'monokai',
|
id: 'monokai',
|
||||||
@@ -116,13 +128,18 @@ const themes: Themes = {
|
|||||||
secondary: '#222218',
|
secondary: '#222218',
|
||||||
highlight: '#49483E',
|
highlight: '#49483E',
|
||||||
background: '#272822',
|
background: '#272822',
|
||||||
comment: '#75715e',
|
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#49483E',
|
color: '#49483E',
|
||||||
backgroundColor: '#3E3D32',
|
backgroundColor: '#3E3D32',
|
||||||
},
|
},
|
||||||
editor: monokai as editor.IStandaloneThemeData,
|
editor: addLogColors(monokai as editor.IStandaloneThemeData, {
|
||||||
|
info: '#a6e22e', // green
|
||||||
|
error: '#f92672', // red
|
||||||
|
warning: '#fd971f', // orange
|
||||||
|
date: '#AB9DF2', // purple
|
||||||
|
exception: '#F1FA8C', // yellow
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
'solarized': {
|
'solarized': {
|
||||||
id: 'solarized',
|
id: 'solarized',
|
||||||
@@ -130,13 +147,18 @@ const themes: Themes = {
|
|||||||
secondary: '#073642', // base02
|
secondary: '#073642', // base02
|
||||||
highlight: '#002b36', // base03
|
highlight: '#002b36', // base03
|
||||||
background: '#002B36', // base03
|
background: '#002B36', // base03
|
||||||
comment: '#586e75', // base01
|
|
||||||
lightOrDark: 'dark',
|
lightOrDark: 'dark',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#93a1a1', // base1
|
color: '#93a1a1', // base1
|
||||||
backgroundColor: '#073642', // base02
|
backgroundColor: '#073642', // base02
|
||||||
},
|
},
|
||||||
editor: solarizedDark as editor.IStandaloneThemeData,
|
editor: addLogColors(solarizedDark as editor.IStandaloneThemeData, {
|
||||||
|
info: '#268bd2', // blue
|
||||||
|
error: '#dc322f', // red
|
||||||
|
warning: '#b58900', // yellow
|
||||||
|
date: '#2aa198', // cyan
|
||||||
|
exception: '#859900', // green
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
'solarized-light': {
|
'solarized-light': {
|
||||||
id: 'solarized-light',
|
id: 'solarized-light',
|
||||||
@@ -144,13 +166,18 @@ const themes: Themes = {
|
|||||||
secondary: '#eee8d5', // base2
|
secondary: '#eee8d5', // base2
|
||||||
highlight: '#FDF6E3', // base3
|
highlight: '#FDF6E3', // base3
|
||||||
background: '#FDF6E3', // base3
|
background: '#FDF6E3', // base3
|
||||||
comment: '#93a1a1', // base1
|
|
||||||
lightOrDark: 'light',
|
lightOrDark: 'light',
|
||||||
highlightedLine: {
|
highlightedLine: {
|
||||||
color: '#586e75', // base01
|
color: '#586e75', // base01
|
||||||
backgroundColor: '#eee8d5', // base2
|
backgroundColor: '#eee8d5', // base2
|
||||||
},
|
},
|
||||||
editor: solarizedLight as editor.IStandaloneThemeData,
|
editor: addLogColors(solarizedLight as editor.IStandaloneThemeData, {
|
||||||
|
info: '#268bd2', // blue
|
||||||
|
error: '#dc322f', // red
|
||||||
|
warning: '#b58900', // yellow
|
||||||
|
date: '#2aa198', // cyan
|
||||||
|
exception: '#859900', // green
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,6 +198,11 @@ interface MonacoThemeProps {
|
|||||||
keyword: Color;
|
keyword: Color;
|
||||||
type: Color;
|
type: Color;
|
||||||
variable: Color;
|
variable: Color;
|
||||||
|
logInfo: Color;
|
||||||
|
logError: Color;
|
||||||
|
logWarning: Color;
|
||||||
|
logDate: Color;
|
||||||
|
logException: Color;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +241,11 @@ export function makeMonacoTheme(
|
|||||||
{ token: 'identifier', foreground: colors.primary },
|
{ token: 'identifier', foreground: colors.primary },
|
||||||
{ token: 'type', foreground: colors.type },
|
{ token: 'type', foreground: colors.type },
|
||||||
{ token: 'comment', foreground: colors.comment },
|
{ token: 'comment', foreground: colors.comment },
|
||||||
|
{ token: 'info.log', foreground: colors.logInfo },
|
||||||
|
{ token: 'error.log', foreground: colors.logError, fontStyle: 'bold' },
|
||||||
|
{ token: 'warning.log', foreground: colors.logWarning },
|
||||||
|
{ token: 'date.log', foreground: colors.logDate },
|
||||||
|
{ token: 'exception.log', foreground: colors.logException },
|
||||||
],
|
],
|
||||||
colors: {
|
colors: {
|
||||||
'editor.background': `#${colors.background}`,
|
'editor.background': `#${colors.background}`,
|
||||||
@@ -216,3 +253,30 @@ export function makeMonacoTheme(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LogColors {
|
||||||
|
info: Color;
|
||||||
|
error: Color;
|
||||||
|
warning: Color;
|
||||||
|
date: Color;
|
||||||
|
exception: Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLogColors(
|
||||||
|
theme: editor.IStandaloneThemeData,
|
||||||
|
logColors: LogColors
|
||||||
|
): editor.IStandaloneThemeData {
|
||||||
|
const colors = Object.fromEntries(
|
||||||
|
Object.entries(logColors).map(([key, color]) => [key, color.substring(1)])
|
||||||
|
) as Record<keyof LogColors, string>;
|
||||||
|
theme.rules.push(
|
||||||
|
...[
|
||||||
|
{ token: 'info.log', foreground: colors.info },
|
||||||
|
{ token: 'error.log', foreground: colors.error, fontStyle: 'bold' },
|
||||||
|
{ token: 'warning.log', foreground: colors.warning },
|
||||||
|
{ token: 'date.log', foreground: colors.date },
|
||||||
|
{ token: 'exception.log', foreground: colors.exception },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,3 +2,7 @@ export const bytebinUrl =
|
|||||||
process.env.REACT_APP_BYTEBIN_URL || 'https://bytebin.lucko.me/';
|
process.env.REACT_APP_BYTEBIN_URL || 'https://bytebin.lucko.me/';
|
||||||
|
|
||||||
export const postUrl = bytebinUrl + 'post';
|
export const postUrl = bytebinUrl + 'post';
|
||||||
|
|
||||||
|
export const languageDetectionUrl =
|
||||||
|
process.env.REACT_APP_LANG_DETECT_URL ||
|
||||||
|
'https://language-detection-service.pastes.dev/';
|
||||||
|
|||||||
49
src/util/detect-language.ts
Normal file
49
src/util/detect-language.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { languageDetectionUrl } from './constants';
|
||||||
|
import { Language } from './language';
|
||||||
|
|
||||||
|
interface DetectedLanguage {
|
||||||
|
languageId: string;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function detectLanguage(id: string): Promise<Language | null> {
|
||||||
|
try {
|
||||||
|
const resp = await fetch(languageDetectionUrl + id);
|
||||||
|
if (resp.ok) {
|
||||||
|
const results = (await resp.json()) as DetectedLanguage[];
|
||||||
|
for (const { languageId, confidence } of results) {
|
||||||
|
if (confidence > 0.5 && lookup[languageId]) {
|
||||||
|
return lookup[languageId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lookup: Record<string, Language> = {
|
||||||
|
ini: 'log', // the model seems to confidently guess log files as ini - log is the more likely option
|
||||||
|
yaml: 'yaml',
|
||||||
|
md: 'markdown',
|
||||||
|
rb: 'ruby',
|
||||||
|
kt: 'kotlin',
|
||||||
|
xml: 'xml',
|
||||||
|
js: 'javascript',
|
||||||
|
html: 'html',
|
||||||
|
ts: 'typescript',
|
||||||
|
json: 'json',
|
||||||
|
php: 'php',
|
||||||
|
py: 'python',
|
||||||
|
rs: 'rust',
|
||||||
|
sql: 'sql',
|
||||||
|
sh: 'shell',
|
||||||
|
cpp: 'cpp',
|
||||||
|
go: 'go',
|
||||||
|
scala: 'scala',
|
||||||
|
dockerfile: 'dockerfile',
|
||||||
|
java: 'java',
|
||||||
|
cs: 'csharp',
|
||||||
|
css: 'css',
|
||||||
|
groovy: 'java',
|
||||||
|
};
|
||||||
|
// missing: csv, ml, ex, pas, bat, lua, groovy, v, jl, pm, prolog, matlab, clj, f90, c, tex, coffee, ps1, hs, mm, cmake, erl, dm, dart, asm, makefile, r, swift, lisp, vba, toml, cbl
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
export const languages = {
|
|
||||||
config: ['yaml', 'json', 'xml', 'ini'],
|
|
||||||
code: [
|
|
||||||
'java',
|
|
||||||
'javascript',
|
|
||||||
'typescript',
|
|
||||||
'python',
|
|
||||||
'kotlin',
|
|
||||||
'cpp',
|
|
||||||
'csharp',
|
|
||||||
'shell',
|
|
||||||
'ruby',
|
|
||||||
'rust',
|
|
||||||
'sql',
|
|
||||||
'go',
|
|
||||||
],
|
|
||||||
web: ['html', 'css', 'php'],
|
|
||||||
misc: ['plain', 'dockerfile', 'markdown'],
|
|
||||||
};
|
|
||||||
|
|
||||||
// missing following the rewrite: toml, properties, log, javastacktrace, groovy, haskell, protobuf
|
|
||||||
// would be good to add these back with custom language definitions
|
|
||||||
|
|
||||||
export const languageIds = Object.values(languages).flat(1);
|
|
||||||
70
src/util/language.ts
Normal file
70
src/util/language.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
export type Language =
|
||||||
|
| 'plain'
|
||||||
|
| 'plaintext'
|
||||||
|
| 'log'
|
||||||
|
| 'yaml'
|
||||||
|
| 'json'
|
||||||
|
| 'xml'
|
||||||
|
| 'ini'
|
||||||
|
| 'java'
|
||||||
|
| 'javascript'
|
||||||
|
| 'typescript'
|
||||||
|
| 'python'
|
||||||
|
| 'kotlin'
|
||||||
|
| 'scala'
|
||||||
|
| 'cpp'
|
||||||
|
| 'csharp'
|
||||||
|
| 'shell'
|
||||||
|
| 'ruby'
|
||||||
|
| 'rust'
|
||||||
|
| 'sql'
|
||||||
|
| 'go'
|
||||||
|
| 'html'
|
||||||
|
| 'css'
|
||||||
|
| 'scss'
|
||||||
|
| 'php'
|
||||||
|
| 'graphql'
|
||||||
|
| 'dockerfile'
|
||||||
|
| 'markdown'
|
||||||
|
| 'proto';
|
||||||
|
|
||||||
|
export const unknownLanguage: Language & 'plain' = 'plain';
|
||||||
|
|
||||||
|
export interface Languages {
|
||||||
|
text: Language[];
|
||||||
|
config: Language[];
|
||||||
|
code: Language[];
|
||||||
|
web: Language[];
|
||||||
|
misc: Language[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const languages: Languages = {
|
||||||
|
text: ['plaintext', 'log'],
|
||||||
|
config: ['yaml', 'json', 'xml', 'ini'],
|
||||||
|
code: [
|
||||||
|
'java',
|
||||||
|
'javascript',
|
||||||
|
'typescript',
|
||||||
|
'python',
|
||||||
|
'kotlin',
|
||||||
|
'scala',
|
||||||
|
'cpp',
|
||||||
|
'csharp',
|
||||||
|
'shell',
|
||||||
|
'ruby',
|
||||||
|
'rust',
|
||||||
|
'sql',
|
||||||
|
'go',
|
||||||
|
],
|
||||||
|
web: ['html', 'css', 'scss', 'php', 'graphql'],
|
||||||
|
misc: ['dockerfile', 'markdown', 'proto'],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const languageIds: Language[] = [
|
||||||
|
...Object.values(languages).flat(1),
|
||||||
|
unknownLanguage,
|
||||||
|
];
|
||||||
|
|
||||||
|
export function isLanguage(lang: string): lang is Language {
|
||||||
|
return languageIds.includes(lang as Language);
|
||||||
|
}
|
||||||
71
src/util/log-language.ts
Normal file
71
src/util/log-language.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import type { languages } from 'monaco-editor';
|
||||||
|
|
||||||
|
// Source:
|
||||||
|
// - https://github.com/emilast/vscode-logfile-highlighter/blob/master/syntaxes/log.tmLanguage
|
||||||
|
// - https://github.com/sumy7/monaco-language-log/blob/main/language-log.js
|
||||||
|
export const logLanguage: languages.IMonarchLanguage = {
|
||||||
|
defaultToken: '',
|
||||||
|
tokenizer: {
|
||||||
|
// prettier-ignore
|
||||||
|
root: [
|
||||||
|
// Trace/Verbose
|
||||||
|
[/\b(Trace)\b:/, 'verbose'],
|
||||||
|
// Serilog VERBOSE
|
||||||
|
[/\[(verbose|verb|vrb|vb|v)]/i, 'verbose'],
|
||||||
|
// Android logcat Verbose
|
||||||
|
[/\bV\//, 'verbose'],
|
||||||
|
// DEBUG
|
||||||
|
[/\b(DEBUG|Debug)\b|\b([dD][eE][bB][uU][gG]):/, 'debug'],
|
||||||
|
// Serilog DEBUG
|
||||||
|
[/\[(debug|dbug|dbg|de|d)]/i, 'debug'],
|
||||||
|
// Android logcat Debug
|
||||||
|
[/\bD\//, 'debug'],
|
||||||
|
// INFO
|
||||||
|
[/\b(HINT|INFO|INFORMATION|Info|NOTICE|II)\b|\b([iI][nN][fF][oO]|[iI][nN][fF][oO][rR][mM][aA][tT][iI][oO][nN]):/, 'info'],
|
||||||
|
// serilog INFO
|
||||||
|
[/\[(information|info|inf|in|i)]/i, 'info'],
|
||||||
|
// Android logcat Info
|
||||||
|
[/\bI\//, 'info'],
|
||||||
|
// WARN
|
||||||
|
[/\b(WARNING|WARN|Warn|WW)\b|\b([wW][aA][rR][nN][iI][nN][gG]):/, 'warning'],
|
||||||
|
// Serilog WARN
|
||||||
|
[/\[(warning|warn|wrn|wn|w)]/i, 'warning'],
|
||||||
|
// Android logcat Warning
|
||||||
|
[/\bW\//, 'warning'],
|
||||||
|
// ERROR
|
||||||
|
[/\b(ALERT|CRITICAL|EMERGENCY|ERROR|FAILURE|FAIL|Fatal|FATAL|Error|EE)\b|\b([eE][rR][rR][oO][rR]):/, 'error'],
|
||||||
|
// Serilog ERROR
|
||||||
|
[/\[(error|eror|err|er|e|fatal|fatl|ftl|fa|f)]/i, 'error'],
|
||||||
|
// Android logcat Error
|
||||||
|
[/\bE\//, 'error'],
|
||||||
|
// ISO dates ("2020-01-01")
|
||||||
|
[/\b\d{4}-\d{2}-\d{2}(T|\b)/, 'date'],
|
||||||
|
// Culture specific dates ("01/01/2020", "01.01.2020")
|
||||||
|
[/\b\d{2}[^\w\s]\d{2}[^\w\s]\d{4}\b/, 'date'],
|
||||||
|
// Clock times with optional timezone ("01:01:01", "01:01:01.001", "01:01:01+01:01")
|
||||||
|
[/\d{1,2}:\d{2}(:\d{2}([.,]\d{1,})?)?(Z| ?[+-]\d{1,2}:\d{2})?\b/, 'date'],
|
||||||
|
// Git commit hashes of length 40, 10, or 7
|
||||||
|
//[/\b([0-9a-fA-F]{40}|[0-9a-fA-F]{10}|[0-9a-fA-F]{7})\b/, 'constant'],
|
||||||
|
// Guids
|
||||||
|
[/[0-9a-fA-F]{8}[-]?([0-9a-fA-F]{4}[-]?){3}[0-9a-fA-F]{12}/, 'constant'],
|
||||||
|
// MAC addresses: 89:A1:23:45:AB:C0, fde8:e767:269c:0:9425:3477:7c8f:7f1a
|
||||||
|
//[/\b([0-9a-fA-F]{2,}[:-])+([0-9a-fA-F]{2,})+\b/, 'constant'],
|
||||||
|
// Constants
|
||||||
|
//[/\b([0-9]+|true|false|null)\b/, 'constant'],
|
||||||
|
// Hex Constants
|
||||||
|
[/\b(0x[a-fA-F0-9]+)\b/, 'constant'],
|
||||||
|
// String constants
|
||||||
|
[/"[^"]*"/, 'string'],
|
||||||
|
[/(?<![\w])'[^']*'/, 'string'],
|
||||||
|
// Colorize rows of exception call stacks
|
||||||
|
[/[\t ]*at[\t ]+.*$/, 'exception'],
|
||||||
|
[/Exception in thread ".*" .*$/, 'exception'],
|
||||||
|
// Exception type names
|
||||||
|
[/\b([a-zA-Z.]*Exception)\b/, 'exception'],
|
||||||
|
// Match Urls
|
||||||
|
[/\b(http|https|ftp|file):\/\/\S+\b\/?/, 'constant'],
|
||||||
|
// Match character and . sequences (such as namespaces) as well as file names and extensions (e.g. bar.txt)
|
||||||
|
//[/(?<![\w/\\])([\w-]+\.)+([\w-])+(?![\w/\\])/, 'constant'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { gzip } from 'pako';
|
import { gzip } from 'pako';
|
||||||
import MIMEType from 'whatwg-mimetype';
|
import MIMEType from 'whatwg-mimetype';
|
||||||
import { bytebinUrl, postUrl } from './constants';
|
import { bytebinUrl, postUrl } from './constants';
|
||||||
import { languageIds } from './highlighting';
|
import { isLanguage, Language } from './language';
|
||||||
|
|
||||||
interface LoadResultSuccess {
|
interface LoadResultSuccess {
|
||||||
ok: true;
|
ok: true;
|
||||||
content: string;
|
content: string;
|
||||||
type?: string;
|
type?: Language;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LoadResultFail {
|
interface LoadResultFail {
|
||||||
@@ -64,13 +64,21 @@ export async function saveToBytebin(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function contentTypeToLanguage(contentType: string) {
|
export function contentTypeToLanguage(
|
||||||
|
contentType: string
|
||||||
|
): Language | undefined {
|
||||||
const { type, subtype: subType } = new MIMEType(contentType);
|
const { type, subtype: subType } = new MIMEType(contentType);
|
||||||
if (type === 'application' && subType === 'json') {
|
if (type === 'application' && subType === 'json') {
|
||||||
return 'json';
|
return 'json';
|
||||||
}
|
}
|
||||||
if (type === 'text' && languageIds.includes(subType.toLowerCase())) {
|
|
||||||
return subType.toLowerCase();
|
let subTypeLower = subType.toLowerCase();
|
||||||
|
if (subTypeLower.startsWith('x-')) {
|
||||||
|
subTypeLower = subTypeLower.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'text' && isLanguage(subTypeLower)) {
|
||||||
|
return subTypeLower;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user