Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
ai-box
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
青山
ai-box
Commits
564294bc
Commit
564294bc
authored
Nov 12, 2024
by
fisherdaddy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feature: 新增diff工具
parent
e390e4a6
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
308 additions
and
0 deletions
+308
-0
package.json
package.json
+1
-0
diff.png
public/assets/icon/diff.png
+0
-0
App.jsx
src/App.jsx
+2
-0
TextDiff.jsx
src/components/TextDiff.jsx
+268
-0
tools.json
src/locales/en/tools.json
+8
-0
tools.json
src/locales/ja/tools.json
+8
-0
tools.json
src/locales/ko/tools.json
+8
-0
tools.json
src/locales/zh/tools.json
+8
-0
DevTools.jsx
src/pages/DevTools.jsx
+2
-0
Home.jsx
src/pages/Home.jsx
+3
-0
No files found.
package.json
View file @
564294bc
...
...
@@ -10,6 +10,7 @@
"dependencies"
:
{
"
@react-oauth/google
"
:
"
^0.12.1
"
,
"
antd
"
:
"
^5.21.6
"
,
"
diff
"
:
"
^7.0.0
"
,
"
dompurify
"
:
"
^3.1.7
"
,
"
html2canvas
"
:
"
^1.4.1
"
,
"
i18next
"
:
"
^23.16.5
"
,
...
...
public/assets/icon/diff.png
0 → 100644
View file @
564294bc
209 KB
src/App.jsx
View file @
564294bc
...
...
@@ -21,6 +21,7 @@ const HandwriteGen = lazy(() => import('./components/HandwriteGen'));
const
ImageBase64Converter
=
lazy
(()
=>
import
(
'./components/ImageBase64Converter'
));
const
QuoteCard
=
lazy
(()
=>
import
(
'./components/QuoteCard'
));
const
LatexToImage
=
lazy
(()
=>
import
(
'./components/LatexToImage'
));
const
TextDiff
=
lazy
(()
=>
import
(
'./components/TextDiff'
));
function
App
()
{
return
(
...
...
@@ -48,6 +49,7 @@ function App() {
<
Route
path=
"/image-base64"
element=
{
<
ImageBase64Converter
/>
}
/>
<
Route
path=
"/quote-card"
element=
{
<
QuoteCard
/>
}
/>
<
Route
path=
"/latex-to-image"
element=
{
<
LatexToImage
/>
}
/>
<
Route
path=
"/text-diff"
element=
{
<
TextDiff
/>
}
/>
<
Route
path=
"*"
element=
{
<
NotFound
/>
}
/>
</
Routes
>
...
...
src/components/TextDiff.jsx
0 → 100644
View file @
564294bc
import
React
,
{
useState
}
from
'react'
;
import
styled
from
'styled-components'
;
import
{
useTranslation
}
from
'../js/i18n'
;
import
SEO
from
'./SEO'
;
const
Container
=
styled
.
div
`
min-height: 100vh;
background: linear-gradient(135deg, #f5f7ff 0%, #ffffff 100%);
padding: 2rem;
position: relative;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
linear-gradient(90deg, rgba(99, 102, 241, 0.05) 1px, transparent 1px),
linear-gradient(rgba(99, 102, 241, 0.05) 1px, transparent 1px);
background-size: 20px 20px;
pointer-events: none;
}
`
;
const
ContentWrapper
=
styled
.
div
`
display: flex;
gap: 2rem;
max-width: 1400px;
margin: 0 auto;
position: relative;
z-index: 1;
@media (max-width: 768px) {
flex-direction: column;
}
`
;
const
InputContainer
=
styled
.
div
`
flex: 1;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 1.5rem;
box-shadow: 0 8px 32px rgba(99, 102, 241, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
display: flex;
flex-direction: column;
gap: 1rem;
`
;
const
TitleLabel
=
styled
.
h2
`
font-size: 1.8rem;
margin-bottom: 1.5rem;
background: linear-gradient(135deg, #6366F1 0%, #4F46E5 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 700;
letter-spacing: -0.02em;
`
;
const
TextArea
=
styled
.
textarea
`
width: 100%;
height: 400px;
padding: 1rem;
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: 8px;
font-size: 14px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
resize: none;
background: rgba(255, 255, 255, 0.9);
&:focus {
outline: none;
border-color: rgba(99, 102, 241, 0.5);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.1);
}
`
;
const
DiffContainer
=
styled
.
div
`
flex: 1;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 1.5rem;
box-shadow: 0 8px 32px rgba(99, 102, 241, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
`
;
const
DiffViewContainer
=
styled
.
div
`
display: flex;
gap: 2rem;
width: 100%;
max-width: 1400px;
margin: 0 auto;
`
;
const
DiffPanel
=
styled
.
div
`
flex: 1;
min-width: 0;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 1.5rem;
box-shadow: 0 8px 32px rgba(99, 102, 241, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
display: flex;
flex-direction: column;
`
;
const
LineNumbersContainer
=
styled
.
div
`
width: 40px;
padding-right: 10px;
text-align: right;
color: #6e7681;
user-select: none;
border-right: 1px solid #d0d7de;
`
;
const
DiffContent
=
styled
.
div
`
display: flex;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
font-size: 14px;
line-height: 1.5;
overflow-x: auto;
white-space: pre;
margin-top: 1rem;
border: 1px solid rgba(99, 102, 241, 0.2);
border-radius: 8px;
background: rgba(255, 255, 255, 0.9);
`
;
const
DiffLine
=
styled
.
div
`
display: flex;
width: 100%;
background-color:
${
props
=>
{
if
(
props
.
$added
)
return
'rgba(46, 160, 67, 0.15)'
;
if
(
props
.
$removed
)
return
'rgba(248, 81, 73, 0.15)'
;
return
'transparent'
;
}};
`;
const LineContent = styled.div`
padding
:
0
8
px
;
flex
:
1
;
color
:
$
{
props
=>
{
if
(
props
.
$added
)
return
'#1a7f37'
;
if
(
props
.
$removed
)
return
'#cf222e'
;
return
'inherit'
;
}};
`;
const LineNumber = styled.div`
color
:
#
6
e7681
;
padding
:
0
8
px
;
text
-
align
:
right
;
user
-
select
:
none
;
min
-
width
:
40
px
;
border
-
right
:
1
px
solid
#
d0d7de
;
`;
const DiffHeader = styled(TitleLabel)`
font
-
size
:
1.4
rem
;
margin
-
bottom
:
1
rem
;
`;
function TextDiff() {
const { t } = useTranslation();
const [oldText, setOldText] = useState('');
const [newText, setNewText] = useState('');
const processText = (text) => {
return text.split('\n');
};
const oldLines = processText(oldText);
const newLines = processText(newText);
const maxLines = Math.max(oldLines.length, newLines.length);
return (
<>
<SEO
title={t('tools.textDiff.title')}
description={t('tools.textDiff.description')}
/>
<Container>
<DiffViewContainer>
<DiffPanel>
<DiffHeader>{t('tools.textDiff.originalText')}</DiffHeader>
<textarea
value={oldText}
onChange={(e) => setOldText(e.target.value)}
placeholder={t('tools.textDiff.originalPlaceholder')}
style={{
width: '100%',
height: '200px',
padding: '8px',
border: '1px solid rgba(99, 102, 241, 0.2)',
borderRadius: '8px',
fontFamily: 'Monaco, Menlo, Consolas, monospace',
resize: 'none'
}}
/>
<DiffContent>
<LineNumber>
{oldLines.map((_, i) => (
<div key={i}>{i + 1}</div>
))}
</LineNumber>
<div style={{ flex: 1 }}>
{oldLines.map((line, i) => (
<DiffLine
key={i}
$removed={!newLines.includes(line)}
>
<LineContent $removed={!newLines.includes(line)}>
{line}
</LineContent>
</DiffLine>
))}
</div>
</DiffContent>
</DiffPanel>
<DiffPanel>
<DiffHeader>{t('tools.textDiff.newText')}</DiffHeader>
<textarea
value={newText}
onChange={(e) => setNewText(e.target.value)}
placeholder={t('tools.textDiff.newPlaceholder')}
style={{
width: '100%',
height: '200px',
padding: '8px',
border: '1px solid rgba(99, 102, 241, 0.2)',
borderRadius: '8px',
fontFamily: 'Monaco, Menlo, Consolas, monospace',
resize: 'none'
}}
/>
<DiffContent>
<LineNumber>
{newLines.map((_, i) => (
<div key={i}>{i + 1}</div>
))}
</LineNumber>
<div style={{ flex: 1 }}>
{newLines.map((line, i) => (
<DiffLine
key={i}
$added={!oldLines.includes(line)}
>
<LineContent $added={!oldLines.includes(line)}>
{line}
</LineContent>
</DiffLine>
))}
</div>
</DiffContent>
</DiffPanel>
</DiffViewContainer>
</Container>
</>
);
}
export default TextDiff;
\ No newline at end of file
src/locales/en/tools.json
View file @
564294bc
...
...
@@ -104,5 +104,13 @@
"fisherai"
:
{
"title"
:
"FisherAI"
,
"description"
:
"The Best Summary Extension for Chrome Browser"
},
"textDiff"
:
{
"title"
:
"Text Diff"
,
"description"
:
"Compare differences between two texts"
,
"originalText"
:
"Original Text"
,
"newText"
:
"New Text"
,
"originalPlaceholder"
:
"Enter original text here..."
,
"newPlaceholder"
:
"Enter new text here..."
}
}
\ No newline at end of file
src/locales/ja/tools.json
View file @
564294bc
...
...
@@ -104,5 +104,13 @@
"fisherai"
:
{
"title"
:
"FisherAI"
,
"description"
:
"最高のChromeブラウザ用要約プラグイン"
},
"textDiff"
:
{
"title"
:
"テキスト差分"
,
"description"
:
"2つのテキストの違いを比較"
,
"originalText"
:
"元のテキスト"
,
"newText"
:
"新しいテキスト"
,
"originalPlaceholder"
:
"元のテキストを入力..."
,
"newPlaceholder"
:
"新しいテキストを入力..."
}
}
\ No newline at end of file
src/locales/ko/tools.json
View file @
564294bc
...
...
@@ -105,5 +105,13 @@
"fisherai"
:
{
"title"
:
"FisherAI"
,
"description"
:
"가장 유용한 Chrome 브라우저 요약 확장 프로그램"
},
"textDiff"
:
{
"title"
:
"텍스트 비교"
,
"description"
:
"두 텍스트의 차이점 비교"
,
"originalText"
:
"원본 텍스트"
,
"newText"
:
"새 텍스트"
,
"originalPlaceholder"
:
"원본 텍스트를 입력하세요..."
,
"newPlaceholder"
:
"새 텍스트를 입력하세요..."
}
}
\ No newline at end of file
src/locales/zh/tools.json
View file @
564294bc
...
...
@@ -103,5 +103,13 @@
"fisherai"
:
{
"title"
:
"FisherAI"
,
"description"
:
"最好用的 Chrome 浏览器摘要插件"
},
"textDiff"
:
{
"title"
:
"文本差异对比"
,
"description"
:
"比较两段文本的差异"
,
"originalText"
:
"原始文本"
,
"newText"
:
"新文本"
,
"originalPlaceholder"
:
"在此输入原始文本..."
,
"newPlaceholder"
:
"在此输入新文本..."
}
}
\ No newline at end of file
src/pages/DevTools.jsx
View file @
564294bc
...
...
@@ -7,6 +7,8 @@ const tools = [
{
id
:
'jsonFormatter'
,
icon
:
'/assets/icon/json-format.png'
,
path
:
'/json-formatter'
},
{
id
:
'urlEncodeDecode'
,
icon
:
'/assets/icon/url-endecode.png'
,
path
:
'/url-encode-and-decode'
},
{
id
:
'imageBase64Converter'
,
icon
:
'/assets/icon/image-base64.png'
,
path
:
'/image-base64'
},
{
id
:
'textDiff'
,
icon
:
'/assets/icon/diff.png'
,
path
:
'/text-diff'
},
];
const
DevTools
=
()
=>
{
...
...
src/pages/Home.jsx
View file @
564294bc
...
...
@@ -7,10 +7,13 @@ const tools = [
{
id
:
'handwrite'
,
icon
:
'/assets/icon/handwrite.png'
,
path
:
'/handwriting'
},
{
id
:
'quoteCard'
,
icon
:
'/assets/icon/quotecard.png'
,
path
:
'/quote-card'
},
{
id
:
'markdown2image'
,
icon
:
'/assets/icon/markdown2image.png'
,
path
:
'/markdown-to-image'
},
{
id
:
'latex2image'
,
icon
:
'/assets/icon/latex2image.png'
,
path
:
'/latex-to-image'
},
{
id
:
'jsonFormatter'
,
icon
:
'/assets/icon/json-format.png'
,
path
:
'/json-formatter'
},
{
id
:
'urlEncodeDecode'
,
icon
:
'/assets/icon/url-endecode.png'
,
path
:
'/url-encode-and-decode'
},
{
id
:
'imageBase64Converter'
,
icon
:
'/assets/icon/image-base64.png'
,
path
:
'/image-base64'
},
{
id
:
'textDiff'
,
icon
:
'/assets/icon/diff.png'
,
path
:
'/text-diff'
},
{
id
:
'openAITimeline'
,
icon
:
'/assets/icon/openai_small.svg'
,
path
:
'/openai-timeline'
},
{
id
:
'modelPrice'
,
icon
:
'/assets/icon/openai_small.svg'
,
path
:
'/llm-model-price'
},
{
id
:
'fisherai'
,
icon
:
'/assets/icon/fisherai.png'
,
path
:
'https://chromewebstore.google.com/detail/fisherai-your-best-summar/ipfiijaobcenaibdpaacbbpbjefgekbj'
,
external
:
true
}
// 新增外部链接
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment