Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion archived/moon/.env.local
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-WCSCZGFL72
NEXT_PUBLIC_GOOGLE_ANALYTICS_ID=G-WCSCZGFL72
MEGA_HOST=http://git.gitmega.com
MEGA_INTERNAL_HOST=http://git.gitmega.com
# MEGA_HOST=http://localhost:8000
# MEGA_INTERNAL_HOST=http://localhost:8000
6 changes: 5 additions & 1 deletion moon/apps/web/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ['@gitmono/eslint-config/base.js', '@gitmono/eslint-config/next.js', 'plugin:storybook/recommended']
extends: ['@gitmono/eslint-config/base.js', '@gitmono/eslint-config/next.js', 'plugin:storybook/recommended'],
rules: {
// 禁用文件最大行数限制
'max-lines': 'off'
}
}
28 changes: 28 additions & 0 deletions moon/apps/web/components/CodeView/BlobView/CodeContent.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

.codeLineNumber {
margin-left: 15px;
margin-right: 25px;
}

.viewChangeTab {
background-color: rgba(53, 53, 53, 0.103);
display: flex;
position: absolute;
height: 50px;
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
}

.viewChangeTabButton {
padding-left: 20px;
padding-right: 20px;
height: 100%;
border-radius: 1rem;
border: none;
background-color: transparent;
}

.viewChangeTabButton:hover,
.viewChangeTabButton:checked {
background-color: rgba(0, 0, 0, 0.121);
}
128 changes: 128 additions & 0 deletions moon/apps/web/components/CodeView/BlobView/CodeContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import Editor from '@/components/CodeView/BlobView/Editor'

import 'github-markdown-css/github-markdown-light.css'

import { useEffect, useRef, useState } from 'react'
import { Highlight, themes } from 'prism-react-renderer'
import { createRoot } from 'react-dom/client'

import styles from './CodeContent.module.css'

// @ts-ignore
const CodeContent = ({ fileContent }) => {
const [__showEditor, setShowEditor] = useState(false)
const [lfs, setLfs] = useState(false)

useEffect(() => {
if (isLfsContent(fileContent)) {
setLfs(true)
}
}, [fileContent])

const lineRef = useRef<HTMLDivElement[]>([])
// @ts-ignore
const handleLineNumberClick = (lineIndex) => {
setShowEditor(prev => {
const newShowEditor = !prev
const codeLineNumber = lineRef.current[lineIndex]

if (newShowEditor) {
const editorContainer = document.createElement('div')

editorContainer.className = 'editor-container'
const root = createRoot(editorContainer)

root.render(<Editor />)
if (codeLineNumber && codeLineNumber.parentNode) {
codeLineNumber.parentNode.insertBefore(editorContainer, codeLineNumber.nextSibling)
}
} else {
const editorContainer = document.querySelector('.editor-container')

if (editorContainer && editorContainer.parentNode) {
editorContainer.parentNode.removeChild(editorContainer)
}
}
return newShowEditor
})
}

function isLfsContent(content: string): boolean {
const lines = content.split('\n')
let foundVersion = false
let foundOid = false
let foundSize = false

for (const line of lines) {
if (line.startsWith('version ')) {
foundVersion = true
} else if (line.startsWith('oid sha256:')) {
foundOid = true
} else if (line.startsWith('size ')) {
foundSize = true
}
if (foundVersion && foundOid && foundSize) {
return true
}
}
return false
}

return (
<div>
<div className={styles.viewChangeTab}>
<button className={styles.viewChangeTabButton}>Code</button>
<button className={styles.viewChangeTabButton}>Blame</button>
</div>

<Highlight theme={themes.github} code={fileContent} language='rust'>
{({ style, tokens, getLineProps, getTokenProps }) => (
<pre
style={{
...style,
padding: '16px',
paddingTop: '70px'
}}
className='overflow-x-auto whitespace-pre rounded-lg bg-gray-100 p-4 text-sm'
>
{!lfs &&
tokens.map((line, i) => (
<div
/* eslint-disable-next-line react/no-array-index-key */
key={i}
{...getLineProps({ line })}
// @ts-ignore
ref={(el) => lineRef.current[i] = el as HTMLDivElement}
>
<button
onClick={() => handleLineNumberClick(i)}
className={styles.lineNumberButton}
style={{
marginLeft: '8px',
backgroundColor: 'rgb(247, 237, 224, 0.7)',
width: '25px',
height: '17px',
lineHeight: '17px',
borderRadius: '3px',
marginTop: '5px',
border: 'none'
}}
>
+
</button>
<span className={styles.codeLineNumber}>{i + 1}</span>
{line.map((token, key) => (
// eslint-disable-next-line react/no-array-index-key
<span key={key} {...getTokenProps({ token })} />
))}
</div>
))}
{lfs && <span>(Sorry about that, but we can’t show files that are this big right now.)</span>}
</pre>
)}
</Highlight>
</div>
)
}

export default CodeContent;
80 changes: 80 additions & 0 deletions moon/apps/web/components/CodeView/BlobView/Editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useEffect } from "react";
import { CodeHighlightNode, CodeNode, registerCodeHighlighting } from "@lexical/code";
import { AutoLinkNode, LinkNode } from "@lexical/link";
import { ListItemNode, ListNode } from "@lexical/list";
import { TRANSFORMERS } from "@lexical/markdown";
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
import { LexicalComposer } from "@lexical/react/LexicalComposer";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";

import "./editor.css";
import ExampleTheme from "./ExampleTheme";
import ToolbarPlugin from "./ToolbarPlugin";


function Placeholder() {
return <div className="editor-placeholder">Writing your comment...</div>;
}

function CodeHighlightPlugin() {
const [editor] = useLexicalComposerContext();

useEffect(() => {
return registerCodeHighlighting(editor);
}, [editor]);
return null;
}

const editorConfig = {
theme: ExampleTheme,
// Handling of errors during update
onError(error: any) {
throw error
},
// Any custom nodes go here
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode
]
}


export default function Editor() {
return (
// @ts-ignore
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<ToolbarPlugin />
<div className="editor-inner">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
// @ts-ignore
ErrorBoundary={undefined}
/>
<AutoFocusPlugin />
<ListPlugin />
<LinkPlugin />
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
<CodeHighlightPlugin />
</div>
</div>
</LexicalComposer>
);
}
69 changes: 69 additions & 0 deletions moon/apps/web/components/CodeView/BlobView/ExampleTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const exampleTheme = {
ltr: "ltr",
rtl: "rtl",
placeholder: "editor-placeholder",
paragraph: "editor-paragraph",
quote: "editor-quote",
heading: {
h1: "editor-heading-h1",
h2: "editor-heading-h2",
h3: "editor-heading-h3",
h4: "editor-heading-h4",
h5: "editor-heading-h5"
},
list: {
nested: {
listitem: "editor-nested-listitem"
},
ol: "editor-list-ol",
ul: "editor-list-ul",
listitem: "editor-listitem"
},
image: "editor-image",
link: "editor-link",
text: {
bold: "editor-text-bold",
italic: "editor-text-italic",
overflowed: "editor-text-overflowed",
hashtag: "editor-text-hashtag",
underline: "editor-text-underline",
strikethrough: "editor-text-strikethrough",
underlineStrikethrough: "editor-text-underlineStrikethrough",
code: "editor-text-code"
},
code: "editor-code",
codeHighlight: {
atrule: "editor-tokenAttr",
attr: "editor-tokenAttr",
boolean: "editor-tokenProperty",
builtin: "editor-tokenSelector",
cdata: "editor-tokenComment",
char: "editor-tokenSelector",
class: "editor-tokenFunction",
"class-name": "editor-tokenFunction",
comment: "editor-tokenComment",
constant: "editor-tokenProperty",
deleted: "editor-tokenProperty",
doctype: "editor-tokenComment",
entity: "editor-tokenOperator",
function: "editor-tokenFunction",
important: "editor-tokenVariable",
inserted: "editor-tokenSelector",
keyword: "editor-tokenAttr",
namespace: "editor-tokenVariable",
number: "editor-tokenProperty",
operator: "editor-tokenOperator",
prolog: "editor-tokenComment",
property: "editor-tokenProperty",
punctuation: "editor-tokenPunctuation",
regex: "editor-tokenVariable",
selector: "editor-tokenSelector",
string: "editor-tokenSelector",
symbol: "editor-tokenProperty",
tag: "editor-tokenProperty",
url: "editor-tokenOperator",
variable: "editor-tokenVariable"
}
};

export default exampleTheme;
Loading