Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
154 changes: 154 additions & 0 deletions components/GitDiffView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import '@git-diff-view/react/styles/diff-view.css';
Comment thread
TechQuery marked this conversation as resolved.
Outdated

import { generateDiffFile } from '@git-diff-view/file';
import { DiffModeEnum, DiffView } from '@git-diff-view/react';
import { computed, observable } from 'mobx';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { Alert, Button, ButtonGroup, Form } from 'react-bootstrap';

export interface TextFileData {
fileName: string;
content: string;
language?: string;
}

export type GitDiffViewProps = Record<'oldFile' | 'newFile', TextFileData>;

@observer
export class GitDiffView extends ObservedComponent<GitDiffViewProps> {
@observable
accessor diffViewMode = DiffModeEnum.Split;

@observable
accessor diffViewWrap = false;

@observable
accessor diffViewHighlight = true;

@observable
accessor expandAll = false;

@computed
get hasNoDiff() {
const { oldFile, newFile } = this.observedProps;

return oldFile.content === newFile.content;
}

@computed
get diffFile() {
const { oldFile, newFile } = this.observedProps;
const { diffViewMode, expandAll } = this;

const file = generateDiffFile(
oldFile.fileName,
oldFile.content,
newFile.fileName,
newFile.content,
oldFile.language || 'text',
newFile.language || 'text',
);

file.init();

if (diffViewMode & DiffModeEnum.Split) {
file.buildSplitDiffLines();
} else {
file.buildUnifiedDiffLines();
}

if (expandAll)
file.onAllExpand(diffViewMode & DiffModeEnum.Split ? 'split' : 'unified');

return file;
}

renderDiff() {
const {
diffViewMode,
diffViewWrap,
diffViewHighlight,
expandAll,
diffFile,
} = this;
Comment thread
TechQuery marked this conversation as resolved.

return (
<>
<div className="d-flex flex-wrap align-items-center gap-2 mb-3">
<ButtonGroup size="sm" aria-label="Diff mode">
<Button
variant={
diffViewMode & DiffModeEnum.Split
? 'primary'
: 'outline-primary'
}
onClick={() => (this.diffViewMode = DiffModeEnum.Split)}
>
Split
</Button>
<Button
variant={
diffViewMode & DiffModeEnum.Split
? 'outline-primary'
: 'primary'
}
onClick={() => (this.diffViewMode = DiffModeEnum.Unified)}
>
Unified
</Button>
</ButtonGroup>

<Form.Check
type="switch"
id="text-file-diff-wrap"
label="Wrap"
checked={diffViewWrap}
onChange={({ currentTarget }) =>
(this.diffViewWrap = currentTarget.checked)
}
/>
<Form.Check
type="switch"
id="text-file-diff-highlight"
label="Highlight"
checked={diffViewHighlight}
onChange={({ currentTarget }) =>
(this.diffViewHighlight = currentTarget.checked)
}
/>
<Form.Check
type="switch"
id="text-file-diff-expand-all"
label="Expand all"
checked={expandAll}
onChange={({ currentTarget }) =>
(this.expandAll = currentTarget.checked)
}
/>
</div>

<DiffView
diffFile={diffFile}
diffViewMode={diffViewMode}
diffViewWrap={diffViewWrap}
diffViewHighlight={diffViewHighlight}
/>
</>
);
}

render() {
const { hasNoDiff } = this;

return hasNoDiff ? (
<>
<Alert variant="danger">No diff found between the two files.</Alert>

<pre>{this.props.oldFile.content}</pre>
</>
) : (
this.renderDiff()
);
}
}
1 change: 0 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default defineConfig(
'simple-import-sort': simpleImportSortPlugin,
'@typescript-eslint': tsEslint.plugin,
react,
// @ts-expect-error https://github.com/vercel/next.js/discussions/84792
'@next/next': nextPlugin,
},
},
Expand Down
81 changes: 37 additions & 44 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,101 +8,94 @@
},
"dependencies": {
"@editorjs/code": "^2.9.4",
"@editorjs/editorjs": "^2.31.5",
"@editorjs/header": "^2.8.8",
"@editorjs/editorjs": "^2.31.6",
"@editorjs/header": "^2.8.9",
"@editorjs/image": "^2.10.3",
"@editorjs/link": "^2.6.2",
"@editorjs/list": "^2.0.9",
"@editorjs/paragraph": "^2.11.7",
"@editorjs/quote": "^2.7.6",
"@git-diff-view/file": "^0.1.5",
"@git-diff-view/react": "^0.1.5",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^16.2.2",
"@sentry/nextjs": "^10.47.0",
"@next/mdx": "^16.2.9",
"@sentry/nextjs": "^10.57.0",
"copy-webpack-plugin": "^14.0.0",
"core-js": "^3.49.0",
"editorjs-html": "^4.0.5",
"file-type": "^22.0.0",
"file-type": "^22.0.1",
"formidable": "^3.5.4",
"idea-react": "^2.0.0-rc.13",
"idea-react": "^2.2.2",
"jsonwebtoken": "^9.0.3",
"koa": "^3.2.0",
"koa": "^3.2.1",
"koa-jwt": "^4.0.4",
"koajax": "^3.3.0",
"less": "^4.6.4",
"less-loader": "^12.3.2",
"less-loader": "^13.0.0",
"lodash": "^4.18.1",
"marked": "^17.0.6",
"marked": "^18.0.5",
"mime": "^4.1.0",
"mobx": "^6.15.0",
"mobx": "^6.16.1",
"mobx-github": "^0.6.2",
"mobx-i18n": "^0.7.2",
"mobx-lark": "^2.8.1",
"mobx-react": "^9.2.1",
"mobx-react": "^9.2.2",
"mobx-react-helper": "^0.5.1",
"mobx-restful": "^2.1.4",
"mobx-restful-table": "^2.6.3",
"next": "^16.2.2",
"next": "^16.2.9",
"next-pwa": "~5.6.0",
"next-ssr-middleware": "^1.1.0",
"next-with-less": "^3.0.1",
"prismjs": "^1.30.0",
"react": "^19.2.4",
"react": "^19.2.7",
"react-bootstrap": "^2.10.10",
"react-bootstrap-editor": "^2.1.1",
"react-dom": "^19.2.4",
"react-dom": "^19.2.7",
"react-editor-js": "^2.1.0",
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.1",
"remark-mdx-frontmatter": "^5.2.0",
"undici": "^8.0.2",
"web-utility": "^4.6.5",
"webpack": "^5.105.4",
"yaml": "^2.8.3"
"undici": "^8.4.1",
"web-utility": "^4.6.6",
"webpack": "^5.107.2",
"yaml": "^2.9.0"
},
"devDependencies": {
"@babel/plugin-proposal-decorators": "^7.29.0",
"@babel/plugin-transform-typescript": "^7.28.6",
"@babel/preset-react": "^7.28.5",
"@cspell/eslint-plugin": "^10.0.0",
"@babel/plugin-proposal-decorators": "^7.29.7",
"@babel/plugin-transform-typescript": "^7.29.7",
"@babel/preset-react": "^7.29.7",
"@cspell/eslint-plugin": "^10.0.1",
"@eslint/js": "^10.0.1",
"@next/eslint-plugin-next": "^16.2.2",
"@next/eslint-plugin-next": "^16.2.9",
"@softonus/prettier-plugin-duplicate-remover": "^1.1.2",
"@stylistic/eslint-plugin": "^5.10.0",
"@types/eslint-config-prettier": "^6.11.3",
"@types/formidable": "^3.5.0",
"@types/formidable": "^3.5.1",
"@types/jsonwebtoken": "^9.0.10",
"@types/koa": "^3.0.2",
"@types/koa": "^3.0.3",
"@types/lodash": "^4.17.24",
"@types/next-pwa": "^5.6.9",
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"eslint": "^10.2.0",
"eslint-config-next": "^16.2.2",
"@types/node": "^24.13.2",
"@types/react": "^19.2.17",
"eslint": "^10.5.0",
"eslint-config-next": "^16.2.9",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-simple-import-sort": "^12.1.1",
"globals": "^17.4.0",
"eslint-plugin-simple-import-sort": "^13.0.0",
"globals": "^17.6.0",
"husky": "^9.1.7",
"jiti": "^2.6.1",
"lint-staged": "^16.4.0",
"prettier": "^3.8.1",
"jiti": "^2.7.0",
"lint-staged": "^17.0.7",
"prettier": "^3.8.4",
"prettier-plugin-css-order": "^2.2.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.58.0"
"typescript-eslint": "^8.61.0"
},
"resolutions": {
"next": "$next"
},
"pnpm": {
"onlyBuiltDependencies": [
"@sentry/cli",
"core-js",
"less",
"sharp",
"unrs-resolver"
]
},
"prettier": {
"singleQuote": true,
"trailingComma": "all",
Expand Down
7 changes: 2 additions & 5 deletions pages/api/Lark/file/[id]/[name].ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Middleware } from 'koa';
import MIME from 'mime';
import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware';
import { Readable } from 'stream';
import { parseJSON } from 'web-utility';

import { CACHE_HOST } from '../../../../../models/configuration';
import { safeAPI } from '../../../core';
Expand Down Expand Up @@ -31,11 +32,7 @@ const downloader: Middleware = async context => {
if (!ok) {
context.status = status;

try {
return (context.body = await response.json());
} catch {
return (context.body = await response.text());
}
return (context.body = parseJSON(await response.text()));
}
const mime = headers.get('Content-Type'),
[stream1, stream2] = body!.tee();
Expand Down
Loading
Loading