Merge pull request '合并至生产分支' (#436) from gitlink_server into pre_gitlink_server

This commit is contained in:
tongChong 2022-08-19 14:00:25 +08:00
commit 2915756e66
253 changed files with 48537 additions and 1575 deletions

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// 使 IntelliSense
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@ -83,6 +83,11 @@ module.exports = {
devtoolModuleFilenameTemplate: (info) =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, "/"),
},
externals: {
"react": "React",
"react-dom": "ReactDOM",
"alex": "Alex",
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
@ -150,7 +155,7 @@ module.exports = {
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
style: true,
},
],
],
@ -206,8 +211,34 @@ module.exports = {
{
loader: require.resolve("sass-loader"),
},
{
loader: 'sass-resources-loader',
options: {
resources: ['src/global.scss']
}
}
],
},
{
test: /\.less$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader', // translates CSS into CommonJS
}, {
loader: 'less-loader', // compiles Less to CSS
options: {
modifyVars: {
'primary-color': '#466aff',
'primary-color-hover': '#6684FE',
'primary-color-active': '#1A47FF',
'link-color': '#466aff',
},
javascriptEnabled: true,
},
}]
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
@ -266,7 +297,7 @@ module.exports = {
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new MonacoWebpackPlugin({
features: ["coreCommands", "find"],
languages: ['plaintext','apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']
languages: ['plaintext', 'apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']
}),
],
// Some libraries import Node modules but don't use them in the browser.

View File

@ -71,6 +71,11 @@ module.exports = {
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, "/"),
},
externals: {
'alex': 'Alex',
"react": "React",
"react-dom": "ReactDOM",
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
@ -147,7 +152,7 @@ module.exports = {
{
libraryName: "antd",
libraryDirectory: "es",
style: "css",
style: true,
},
],
],
@ -208,8 +213,31 @@ module.exports = {
{
loader: require.resolve("sass-loader"),
},
{
loader: 'sass-resources-loader',
options: {
resources: ['src/global.scss']
}
}
],
},
{
test: /\.less$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader', // translates CSS into CommonJS
}, {
loader: 'less-loader', // compiles Less to CSS
options: {
modifyVars: {
'primary-color': '#466aff',
'link-color': '#466aff',
},
javascriptEnabled: true,
},
}]
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules

1046
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"actioncable": "^5.2.4-3",
"ahooks": "^2.10.14",
"antd": "^3.26.15",
"array-flatten": "^2.1.2",
"autoprefixer": "7.1.6",
@ -51,6 +52,8 @@
"js-base64": "^2.5.2",
"js2wordcloud": "^1.1.12",
"katex": "^0.11.1",
"less": "^3.13.1",
"less-loader": "^4.1.0",
"lodash": "^4.17.21",
"loglevel": "^1.6.8",
"marked": "^1.0.0",
@ -108,6 +111,7 @@
"sass-loader": "7.3.1",
"save-dev": "0.0.1-security",
"scroll-into-view": "^1.14.2",
"sha1": "^1.1.1",
"showdown": "^1.9.1",
"showdown-katex": "^0.8.0",
"slick-carousel": "^1.8.1",
@ -205,12 +209,13 @@
"concat": "^1.0.3",
"happypack": "^5.0.1",
"mockjs": "^1.1.0",
"node-sass": "^4.12.0",
"node-sass": "^4.14.1",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"purgecss": "^2.1.2",
"react-json-view": "^1.21.3",
"reqwest": "^2.0.5",
"resize-observer-polyfill": "^1.5.1",
"sass-resources-loader": "^2.2.5",
"terser-webpack-plugin": "^2.3.5",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.42.1",

View File

@ -1365,12 +1365,12 @@ table {
border: 0;
}
a:link,
a:visited {
a:link,a:visited{
text-decoration: none;
color: #05101a;
}
ol,
ul,
li {
@ -2466,7 +2466,7 @@ a.hoverLine:hover{
}
a:hover{
color: #466AFF !important;
color: #6684FE;
}
.color-grey-98 {

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1652927945088') format('woff2'),
url('iconfont.woff?t=1652927945088') format('woff'),
url('iconfont.ttf?t=1652927945088') format('truetype');
src: url('iconfont.woff2?t=1653550272457') format('woff2'),
url('iconfont.woff?t=1653550272457') format('woff'),
url('iconfont.ttf?t=1653550272457') format('truetype');
}
.iconfont {
@ -13,6 +13,10 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-tuichuicon:before {
content: "\e936";
}
.icon-bangzhuzhongxinicon1:before {
content: "\e935";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,13 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "29934961",
"name": "退出icon",
"font_class": "tuichuicon",
"unicode": "e936",
"unicode_decimal": 59702
},
{
"icon_id": "29811507",
"name": "帮助中心icon",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,31 +1,38 @@
<!DOCTYPE html>
<html lang="zh-CN" class="notranslate translated-ltr" translate="no">
<head>
<meta charset="utf-8">
<meta name=”Keywords” Content=”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″>
<meta name="Keywords" Content="gitLink,GitLink,gitlink,trustie,trustieforge,forge,确实让创建更美好,协同开发平台">
<meta name=”Keywords” Content=”TrustieOpenSourceProject″>
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
<meta name="theme-color" content="#000000">
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link href="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.13/bundle/alex.all.global.min.css" rel="stylesheet"/>
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css">
<%= htmlWebpackPlugin.tags.headTags %>
</head>
<body>
<!--用于markdown转html -->
<div id="md_div" style="display: none;"></div>
<div id="root" class="page -layout-v -fit widthunit"></div>
<div id="picture_display" style="display: none;"></div>
<script src="%PUBLIC_URL%js/react.development.js"></script>
<script src="%PUBLIC_URL%js/react-dom.development.js"></script>
<script src="%PUBLIC_URL%js/jquery-1.8.3.min.js"></script>
<script src="%PUBLIC_URL%js/js_min_all.js"></script>
<script src="%PUBLIC_URL%js/codemirror/codemirror.js"></script>
<script src="%PUBLIC_URL%js/editormd/editormd.min.js"></script>
<script src="%PUBLIC_URL%js/codemirror/merge/merge.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/moment/2.29.4/moment.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/alipay/alex/2.0.13/bundle/alex.all.global.min.js"></script>
<%= htmlWebpackPlugin.tags.bodyTags %>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -60,8 +60,13 @@ body {
.ant-progress-textno {
color: #f5222d;
}
.CodeMirror pre.CodeMirror-line{
font-size: 16px!important;
.CodeMirror .CodeMirror-lines pre.CodeMirror-line{
font-size: 16px;
line-height: 20px;
font-family:"PingFang SC", "Segoe UI", Helvetica, Arial, "Apple Color Emoji", SimHei, SimSun, sans-serif, "Microsoft YaHei";
}
.react-codemirror2 .CodeMirror .CodeMirror-lines pre.CodeMirror-line{
font-size: 12px;
}
/* md多空格 */
.markdown-body p {

View File

@ -18,6 +18,7 @@ import moment from 'moment'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import SiderBar from './forge/Component/SiderBar';
import SiderBarHelp from './glcc/siderBarHelp';
// import GlccModal from './modules/glccModal';
import { SnackbarHOC } from 'educoder';
import { initAxiosInterceptors } from './AppConfig'
@ -292,6 +293,7 @@ class App extends Component {
<ConfigProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
{/* <GlccModal /> */}
{!pathName || (pathName && pathName.indexOf("glcc") === -1) ? <SiderBar /> : <SiderBarHelp/>}
{/* <Router> */}
<Switch>

View File

@ -89,8 +89,13 @@ export function initAxiosInterceptors(props) {
if (response.data.status === 404) {
let responseURL = response.request ? response.request.responseURL:'';
// 组织和个人的拥有情况404不跳转
if (responseURL.indexOf('/api/users/') === -1 && responseURL.indexOf('/api/organizations/') === -1 ) {
locationurl('/nopage');
// 邀请页面不进行404跳转
if( window.location.pathname.includes('/invite') && (responseURL.includes('/simple.json')||responseURL.includes('/detail.json')||responseURL.includes('/menu_list.json'))){
}else{
locationurl('/nopage');
}
}
}

View File

@ -118,7 +118,7 @@ export function timeAgo(backDate) {
var days = Math.floor(time / (1000 * 60 * 60 * 24));
var hours = Math.floor((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((time % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
// var seconds = Math.floor((time % (1000 * 60 * 60)) / 1000);
if (time <= 0) {
return "刚刚";
}
@ -129,10 +129,10 @@ export function timeAgo(backDate) {
return hours + "小时前";
}
if (minutes) {
return minutes + "分前";
}
if (seconds) {
return seconds + "秒前";
return minutes + "分钟前";
}
// if (seconds) {
// return seconds + "秒前";
// }
return "刚刚";
}

View File

@ -25,4 +25,18 @@ export function IEVersion(){
}else{
return -1;//不是ie浏览器
}
}
}
export function windowsOrMac(){
let agent = navigator.userAgent.toLowerCase();
let isMac = /macintosh|mac os x/i.test(navigator.userAgent);
if(agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0){
return 'win32';
}
if(agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0){
return 'win64';
}
if(isMac){
return 'mac';
}
}

View File

@ -11,13 +11,28 @@ export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const local = 'https://testforgeplus.trustie.net';
if (isDev) {
return `${local}/${path}`
return `${local}${path}`
}
return `${path}`;
}
export function getImageUrlAbsolute(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const local = 'https://testforgeplus.trustie.net';
const prod = window.location.origin;
if (isDev) {
return `${local}${path}`
}else{
return `${prod}${path}`;
}
}
export function numFormat(num, digits){
let d = digits || 1;
var si = [

View File

@ -4,7 +4,7 @@
export {
getUploadLogoActionUrl as getUploadLogoActionUrl,numFormat as numFormat,
getImageUrl as getImageUrl,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
getImageUrl as getImageUrl,getImageUrlAbsolute as getImageUrlAbsolute,getImage as getImage, getmyUrl as getmyUrl, getRandomNumber as getRandomNumber, getUrl as getUrl, publicSearchs as publicSearchs, getRandomcode as getRandomcode, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrltwo as getUploadActionUrltwo, getUploadActionUrlthree as getUploadActionUrlthree, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST, htmlEncode as htmlEncode, getupload_git_file as getupload_git_file, getcdnImageUrl as getcdnImageUrl,
turnbar,returnbar
@ -78,3 +78,5 @@ export { default as ImageLayer2 } from './hooks/ImageLayer2'
// 外部
export { CNotificationHOC as CNotificationHOC } from '../modules/courses/common/CNotificationHOC'
export { IEVersion as IEVersion , windowsOrMac as windowsOrMac} from './IEVersion'

View File

@ -0,0 +1,117 @@
/* eslint-disable react/jsx-no-duplicate-props */
import React, { useState } from 'react';
import * as ReactDOM from 'react-dom';
import { Modal, Button } from 'antd';
import './index.scss';
InitModal.defaultProps = {
okText: '确认', //
cancelText: '取消', //
className: '', //
inputId: 'copyText', //ID
};
// 使
export default function DelModal(props) {
renderModal({ ...props, type: 'delete' })
}
// 使
export function Confirm(props) {
renderModal({ ...props, type: 'confirm' })
}
function renderModal(props) {
const { type, afterClose } = props;
const div = document.createElement('div');
document.body.appendChild(div);
function destroy() {
afterClose && afterClose();
const unmountResult = ReactDOM.unmountComponentAtNode(div);
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
}
function modalType(type) {
if (type === 'delete') {
return <InitModal
title="删除"
contentTitle="确定要删除吗?"
okText="确认删除"
{...props}
afterClose={destroy}
contentTitle={<React.Fragment>
<i className="red-circle iconfont icon-shanchu_tc_icon mr3"></i>
{props.contentTitle}
</React.Fragment>}
/>
} else if (type === 'confirm') {
return <InitModal title="选择" afterClose={destroy} {...props} />
} else {
return <InitModal title="选择" afterClose={destroy} {...props} />
}
}
function render() {
setTimeout(() => {
ReactDOM.render(
modalType(type),
div,
);
});
}
render();
}
//
function InitModal({
onCancel,
onOk,
title,
contentTitle,
content,
okText,
cancelText,
afterClose,
className,
}) {
const [visible, setVisible] = useState(true);
function onCancelModal() {
setVisible(false);
onCancel && onCancel()
}
function onSuccess() {
setVisible(false);
onOk && onOk();
}
return (
<Modal
visible={visible}
onCancel={onCancelModal}
afterClose={afterClose}
title={title}
className={`myself-modal ${className}`}
centered
footer={[
<Button type="default" key="back" onClick={onCancelModal}>
{cancelText}
</Button>,
<Button className="foot-submit" key="submit" onClick={onSuccess}>
{okText}
</Button>,
]}
>
<div>
{contentTitle && <p className="content-title">{contentTitle}</p>}
<p className="content-descibe">{content}</p>
</div>
</Modal>
)
}

View File

@ -0,0 +1,63 @@
.myself-modal {
.ant-modal-header {
padding: 9px 24px;
background: #f8f8f8;
border-bottom: 1px solid #eee;
}
.ant-modal-title {
text-align: left;
}
.ant-modal-close {
top: 0px !important;
}
.ant-modal-close-x {
font-size: 24px;
}
.ant-modal-body {
text-align: center;
}
.content-title {
display: flex;
justify-content: center;
align-items: center;
margin: 2rem 0 1rem !important;
font-size: 16px;
color: #333;
letter-spacing: 0;
line-height: 29px;
font-weight: 400;
}
.red-circle {
align-self: flex-start;
color: #ca0002;
font-size: 1.5rem !important;
}
.content-descibe {
font-size: 14px;
color: #666;
line-height: 33px;
font-weight: 400;
}
.ant-modal-footer {
padding: 2rem 0;
text-align: center;
border: 0;
.ant-btn {
width: 6rem;
}
}
.foot-submit {
margin-left: 3rem;
color: #df0002;
&:hover {
border-color: #df0002;
}
}
.ant-btn-default:hover,
.ant-btn-default:active,
.ant-btn-default:focus {
background: #f3f4f6;
color: #333;
border-color: #d0d0d0;
}
}

View File

@ -135,7 +135,7 @@ class Activity extends Component{
const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%";
return(
<div className="main">
<div className="main mt20">
<div className="normalBox">
<div class="normalBox-title">概览</div>

View File

@ -80,7 +80,7 @@ function AddMember({getID,login,showNotification}){
<AutoComplete
dataSource={source}
value={searchKey}
style={{ width: 300 }}
style={{ width: 250 }}
onChange={changeInputUser}
onSelect={selectInputUser}
placeholder="搜索需要添加的用户..."
@ -92,7 +92,8 @@ function AddMember({getID,login,showNotification}){
onClick={addCollaborator}
className="ml15"
>
<Icon type="plus" size="16"></Icon>
{/* <Icon type="plus" size="16"></Icon> */}
<i className="iconfont icon-tianjiafangda mr3"></i>
添加成员
</Button>
</div>

View File

@ -276,36 +276,36 @@ li.ant-menu-item{
.-task-sidebar {
position: fixed;
width: 40px;
right: 10px;
right: 30px;
bottom: 80px;
z-index: 10;
}
@media screen and (max-width: 1920px){
.-task-sidebar{
right:220px;
}
}
@media screen and (max-width: 1750px){
.-task-sidebar{
right:160px;
}
}
@media screen and (max-width: 1650px){
.-task-sidebar{
right:115px;
}
}
@media screen and (max-width: 1550px){
.-task-sidebar{
right:90px;
}
}
@media screen and (max-width: 1450px){
.-task-sidebar{
right:45px;
}
}
// @media screen and (max-width: 1920px){
// .-task-sidebar{
// right:220px;
// }
// }
// @media screen and (max-width: 1750px){
// .-task-sidebar{
// right:160px;
// }
// }
// @media screen and (max-width: 1650px){
// .-task-sidebar{
// right:115px;
// }
// }
// @media screen and (max-width: 1550px){
// .-task-sidebar{
// right:90px;
// }
// }
// @media screen and (max-width: 1450px){
// .-task-sidebar{
// right:45px;
// }
// }
@media screen and (max-width: 1200px){
.-task-sidebar{
right:0px;

View File

@ -3,29 +3,22 @@ import { AlignCenter } from '../layout';
import { Button } from 'antd';
import Modals from '../PublicModal/Index';
function DeleteBox({
visible ,
onCancel ,
onSuccess ,
title ,
subTitle,
content
}) {
function DeleteBox({visible, onCancel, onSuccess, title, subTitle, content}) {
return(
<Modals
title={title}
<Modals
title={title}
btn={
<div>
<Button size={'large'} onClick={onCancel}>取消</Button>
<Button type={"danger"} size={"large"} onClick={onSuccess}>确认删除</Button>
</div>
}
onCancel={onCancel}
}
onCancel={onCancel}
visible={visible}
>
<div className="desc">
<AlignCenter className="descMain"><i className="iconfont icon-shanchu_tc_icon mr10"></i>{content}</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}>删除后未来事件将不会推送至此Webhook地址<span title={subTitle}>{subTitle}</span></p>
<AlignCenter className="descMain"><i className="iconfont icon-shanchu_tc_icon mr10 font-36" style={{color: '#ca0002'}}></i>{content}</AlignCenter>
<p className="task-hide-2" style={{WebkitLineClamp:5}}><span title={subTitle}>{subTitle}</span></p>
</div>
</Modals>
)

View File

@ -55,7 +55,7 @@ function LanguagePower({owner,projectsId}){
{
array && array.map((item,key)=>{
return(
<span style={{width:item.percent,backgroundColor:item.color}}></span>
<span key={item.id||key} style={{width:item.percent,backgroundColor:item.color}}></span>
)
})
}
@ -66,7 +66,7 @@ function LanguagePower({owner,projectsId}){
{
array.map((item,key)=>{
return(
<span><i className="zero" style={{backgroundColor:`${item.color}`}}></i><span>{item.name}</span><span>{item.percent}</span></span>
<span key={item.id||key}><i className="zero" style={{backgroundColor:`${item.color}`}}></i><span>{item.name}</span><span>{item.percent}</span></span>
)
})
}

View File

@ -73,8 +73,8 @@ export const Blueline = styled.a`{
height:30px;
line-height:28px;
border-radius:2px;
border:1px solid rgba(80,145,255,1);
color:rgba(80,145,255,1);
border:1px solid #466AFF;
color:#466AFF;
padding:0px 12px;
display:inline-block;
}`
@ -117,7 +117,8 @@ export const Blueback = styled.a`{
height:30px;
line-height:30px;
border-radius:2px;
background-color:rgba(80,145,255,1);
// background-color:rgba(80,145,255,1);
background-color:#466AFF;
color:#fff!important;
padding:0px 12px;
display:inline-block;

View File

@ -136,7 +136,7 @@ function About(props, ref) {
params:{password:authorityVal}
}).then(result=>{
setIsSpining(false);
if(result){
if(result && result.data.status === 0){
setStep(result.data.step);
}
}).catch(error=>{setIsSpining(false)});
@ -149,7 +149,10 @@ function About(props, ref) {
axios.post(url).then(result=>{
setIsSpining(false);
if(result && result.data.status === 0){
props.history.push(`/${owner}/${projectsId}/devops`);
props.history.push({
pathname: `/${owner}/${projectsId}/devops`,
state:{open_devops:true}
});
// open_devops
let { changeOpenDevops } = props;
changeOpenDevops && changeOpenDevops(true);

View File

@ -1,13 +1,14 @@
import React , { useEffect } from 'react';
import React, {useEffect, useState} from 'react';
import { WhiteBack } from '../Component/layout';
import './ops.scss';
import devops from '../Images/devops.png';
import { Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import axios from 'axios';
const About = Loadable({
loader: () => import('./About'),
loader: () => import('./subIndex'),
loading: Loading,
})
const New = Loadable({
@ -32,11 +33,61 @@ const Params = Loadable({
})
export default ((props)=>{
const {isManager, project} = props;
const { projectsId, owner } = props.match.params;
const [jianmu_devops, setJianmu_devops] = useState(undefined);
useEffect(()=>{
// simplejianmu_devops
const url = `/${owner}/${projectsId}/simple.json`;
axios.get(url).then((result) => {
if (result && result.data) {
result.data.jianmu_devops && setJianmu_devops(encodeURIComponent(result.data.jianmu_devops));
}
})
}, [])
useEffect(() => {
window.addEventListener("message", iframeHeight, false);
return () => {
window.removeEventListener("message", iframeHeight, false);
}
})
function iframeHeight(e){
if (e && e.data && typeof(e.data) === "string") {
let myHeight = JSON.parse(e.data);
if (document.querySelector("#devopsIframe")) {
document.querySelector("#devopsIframe").height = myHeight.height;
}
}
}
function iframeLoad() {
try {
let myIframe = document.getElementById("devopsIframe");
if (myIframe.contentDocument) {
myIframe.height = myIframe.contentDocument.querySelector('.el-main').clientHeight + 260;
}
} catch (err) {
console.error(err);
}
}
return(
<WhiteBack className="opsPanel">
<Switch {...props}>
<WhiteBack className={`opsPanel ${isManager ? 'main' : ''}`}>
{/* 嵌入devops */}
{jianmu_devops && project && isManager && <iframe title={`devopsIframe`} src={`${project.jianmu_devops_url}/oauth2/authorize?code=${jianmu_devops}`} id={`devopsIframe`} frameBorder="0" name={`devopsIframe`} width="100%" onLoad={iframeLoad} height={'auto'}></iframe>}
{!isManager && <div className='nullJurisdictionBox'>
<div className='jurTil font-16'>引擎配置</div>
<div className='jurCont mt25'>
<img src={devops} alt="" width={110}/>
<div className='font-18 mt30'>暂无权限仅仓库管理员可访问</div>
</div>
</div>}
{/* 旧引擎页面 */}
{/* <Switch {...props}>
<Route path="/:owner/:projectsId/devops/params"
render={
(p) => (<Params {...props} {...p}/>)
@ -52,7 +103,6 @@ export default ((props)=>{
(p) => (<New {...props} {...p}/>)
}
></Route>
<Route path="/:owner/:projectsId/devops/list/:branch"
render={
(p) => (<Stucture {...props} {...p}/>)
@ -63,13 +113,14 @@ export default ((props)=>{
(p) => (<New {...props} {...p}/>)
}
></Route>
{/* 原本的两种合为一个 */}
//
<Route path="/:owner/:projectsId/devops"
render={
(p) =>{return( p.location && p.location.state && p.location.state.open_devops?<Dispose {...props} {...p}/>:<About {...props} {...p}/>)}
// (p) =>{return( p.location && p.location.state && p.location.state.open_devops?<Dispose {...props} {...p}/>:<About {...props} {...p}/>)}
(p) =>{return(<About {...props} {...p}/>)}
}
></Route>
</Switch>
</Switch> */}
</WhiteBack>
)
})

View File

@ -104,7 +104,7 @@ function Params(props){
<Banner>
<FlexAJ>
<span className="font-18">引擎 - 参数管理</span>
<Link to={`/${owner}/${projectsId}/devops`} className="font-14 color-grey-9 ml20">返回</Link>
<Link to={{pathname:`/${owner}/${projectsId}/devops`,state:{open_devops:true}}} className="font-14 color-grey-9 ml20">返回</Link>
</FlexAJ>
</Banner>
<Div className="disposeList">

View File

@ -290,7 +290,7 @@ function Structure(props,ref){
<Banner>
<FlexAJ>
<span>构建列表</span>
<Link to={`/${owner}/${projectsId}/devops`} className="font-15 color-grey-9">返回</Link>
<Link to={{pathname:`/${owner}/${projectsId}/devops`,state:{open_devops:true}}} className="font-15 color-grey-9">返回</Link>
</FlexAJ>
</Banner>
<Div>

View File

@ -275,7 +275,10 @@ function disposePipeline(props){
...params
}).then(result=>{
if(result){
props.history.push(`/${owner}/${projectsId}/devops`);
props.history.push({
pathname:`/${owner}/${projectsId}/devops`,
state:{open_devops:true}
});
}
setLoading(false);
}).catch(error=>{

View File

@ -2,6 +2,7 @@
{
margin:20px auto;
width: 1200px;
min-height: 500px;
// 开始激活页面
.activatePanel{
display: flex;
@ -521,4 +522,22 @@
border:1px solid #999;
color:#999 ;
}
}
.nullJurisdictionBox{
color:#333333;
.jurTil{
width:1200px;
padding: 15px 16px;
background-color:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:3px 3px 0px 0px;
}
.jurCont{
width:1200px;
height:317px;
padding-top: 45px;
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
text-align: center;
}
}

View File

@ -0,0 +1,13 @@
import React from 'react';
import About from './About';
import Dispose from './Dispose';
function SubIndex(props) {
return(
props && props.open_devops ?
<Dispose {...props} />
:
<About {...props} />
)
}
export default SubIndex;

View File

@ -1,7 +1,8 @@
import axios from 'axios';
//
export const getBranch = async (id,owner)=>{
return (await axios.get(`/${owner}/${id}/branches.json`)).data;
let res = await axios.get(`/${owner}/${id}/branches.json`);
return res&&res.data;
}
//
export const getTag = async (id,owner)=>{

View File

@ -204,8 +204,7 @@ class NewHeader extends Component {
}else{
str = match.path;
}
if (url && str === url) {
if (url && (str === url || (str === url+'/'))) {
return true
}else {
return false

View File

@ -190,11 +190,11 @@ function NoticeContent({ visible, showNotification, resetUserInfo, current_user:
className='hoverNotice-body' // className
onPullRefresh={() => { setNoticePage(noticePage + 1); }} //ajaxfunction
// type={2} //
count={noticeUnreadList.length} //
count={noticeUnreadList&&noticeUnreadList.length} //
pageSize={10} //
>
{
noticeUnreadList.map(item => {
Array.isArray(noticeUnreadList)&& noticeUnreadList.map(item => {
return (
<div key={item.id + Math.random()} className="noticeCont-back" onClick={() => { readItem(item) }}>
<div className={`noticeCont ${item.notification_url?'pointer':''}`}>

View File

@ -27,7 +27,7 @@
margin-left: 15px;
}
.currentMenu{
.ant-dropdown-menu.currentMenu{
width: 120px;
text-align: center;
padding:0px;
@ -62,7 +62,8 @@
color: #666;
&:hover{
color: #fff;
background: #4CACFF;
// background: #4CACFF;
background: #829BFF;
}
}
}

BIN
src/forge/Images/devops.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/forge/Images/img1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

92
src/forge/Invite/Index.js Normal file
View File

@ -0,0 +1,92 @@
import React, { useState, useEffect } from 'react';
import { Modal, Button, } from 'antd';
import { Base64 } from 'js-base64';
import './index.scss';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder'
import axios from 'axios';
const identify = {
manager: "管理员",
developer: "开发者",
reporter: "报告者",
owner: "所有者",
}
function Invite({ history, current_user, match, projectDetail, showNotification }) {
if (!current_user.login) {
let { pathname, search } = window.location;
window.location.href = `/login?go_page=${pathname}${search}`;
}
let permission = projectDetail && projectDetail.permission;
const { projectsId, owner } = match.params;
let inviteString = window.location.search && window.location.search.split('?invite=')[1];
let data = inviteString && JSON.parse(Base64.decode(inviteString));
const [detail, setDetail] = useState({});
const [visible, setVisible] = useState(true);
useEffect(() => {
if (permission && detail.role && detail.role == permission.toLocaleLowerCase() || permission == 'Owner') {
showNotification(`您已经是${identify[detail.role]}`)
setTimeout(() => {
history.push(`/${owner}/${projectsId}`);
}, 2000)
} else {
setVisible(true)
}
}, [permission, detail.role]);
useEffect(() => {
const url = `/${owner}/${projectsId}/project_invite_links/show_link.json?invite_sign=${data.sign}`;
axios.get(url).then(res => {
if (res && res.data) {
setDetail(res.data);
} else {
showNotification('查询邀请链接失败');
}
})
}, [])
function accept() {
const url = `/${owner}/${projectsId}/project_invite_links/redirect_link.json?invite_sign=${data.sign}`;
axios.post(url).then(res => {
if (res && res.data.message == 'success') {
if (detail.is_apply) {
setVisible(false);
Modal.success({ content: '提交申请成功,请等待该仓库管理员审核' });
} else {
history.push(`/${owner}/${projectsId}`);
}
}
}).catch(error => { })
}
return (
<div className="">
{data && <Modal
visible={visible}
className="invite_development"
title={<div className="ownerImage"><img src={detail.project && getImageUrl(detail.project.owner.image_url)} /></div>}
width="548px"
closable={true}
onCancel={() => { setVisible(false); history.push(`/${current_user.login}`) }}
centered
okText={'接受'}
cancelText={'拒绝'}
onOk={accept}
maskClosable={false}
>
<Link className="invite_project link" target="_blank" to={`/${data.ownerLogin}/${data.projectId}`}>{detail.project && detail.project.owner.name}/{detail.project && detail.project.name}</Link>
<div className="invite_content">
<Link className="link" to={`/${detail.user && detail.user.login}`}>{detail.user && detail.user.name}</Link> {identify[detail.role]}
是否接受邀请
</div>
</Modal>}
</div>
)
}
export default Invite;

100
src/forge/Invite/index.scss Normal file
View File

@ -0,0 +1,100 @@
.invite_development {
font-family: PingFang SC;
height: 366px;
background-image: linear-gradient(
359.37deg,
#ebf3ff 0%,
#eff5ff 55.01%,
#cfdeff 100%
);
border: 1.5px solid #ffffff;
border-radius: 4px;
.ant-modal-close{
top:0 !important;
}
.ant-modal-close-x{
font-size: 30px;
color: #666;
width: 48px;
height: 48px;
line-height: 48px;
transform: scaleX(1.05);
}
.ant-modal-content {
background-color: inherit;
}
.ant-modal-header {
background: inherit;
border: 0;
padding-bottom: 0;
}
.ownerImage {
position: relative;
top:-66px;
width: 100px;
height: 100px;
background-color: #e9f1ff;
border: 1px solid #ffffff;
border-radius: 50%;
margin:0 auto;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
img{
width: 100%;
}
}
.invite_project {
display: block;
margin: 0 auto;
font-weight: 500;
font-size: 20px;
line-height: 22px;
text-align: center;
margin-bottom: 30px;
}
.invite_content {
width: 422px;
padding: 24px 48px;
background-color: rgba(225, 231, 255, 0.71);
border-radius: 4px 4px 0px 0px;
margin: 10px auto;
font-weight: 500;
font-size: 17px;
line-height: 32px;
text-align: center;
color: #151d40;
}
.link {
cursor: pointer;
color: $primary-color;
&:hover {
color: $primary-color-hover;
}
}
.ant-modal-footer {
text-align: center;
border: 0;
padding-bottom: 55px;
button {
width: 150px;
height: 42px;
border-radius: 5px;
font-size: 15px;
&:first-child {
background-color: rgba(196, 0, 14, 0.07);
border: 1px solid;
border-color: #f60011;
color: #f60011;
&:hover {
opacity: 0.75;
}
}
& + button {
margin-left: 30px;
}
}
}
}

View File

@ -192,7 +192,7 @@ function CoderDepot(props){
const url = `/${owner}/${projectsId}/sub_entries.json`;
axios.get(url, {
params:{
filepath:path,
filepath:returnbar(path),
ref:ref || branchName,
type
}
@ -272,8 +272,9 @@ function CoderDepot(props){
if(type!=="submodule"){
let enBranch = branchName || defaultBranch;
let checkvalue = turnbar(enBranch);
let enPath = turnbar(path);
setType(type);
props.history.push(`/${owner}/${projectsId}${`/tree/${checkvalue}`}${path?`/${path}`:""}`);
props.history.push(`/${owner}/${projectsId}${`/tree/${checkvalue}`}${enPath?`/${enPath}`:""}`);
}
}
@ -332,6 +333,8 @@ function CoderDepot(props){
}
}
let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
@ -442,6 +445,7 @@ function CoderDepot(props){
issuesFlag &&
<a onClick={createIssue}>+ 疑修</a>
}
</div>
{ fileOperate &&
<Dropdown
@ -492,6 +496,7 @@ function CoderDepot(props){
dirInfo.map((item,key)=>{
return(
<Catalogue
key={item.id||key}
owner={owner}
item={item}
projectsId={projectsId}
@ -521,7 +526,7 @@ function CoderDepot(props){
(dirInfo && dirInfo.length === 0) && !fileInfo ? <Nodata _html="暂未发现文件"/> :""
}
{/* readme文件显示(显示文件详情时不显示readme文件) */}
{ (readme && readme.content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
{ (readme && readme.replace_content) ? <ReadMe ChangeFile={ChangeFile} readme={readme} operate={props && (props.isManager || props.isDeveloper) && projectDetail.type !==2 } history={props.history} /> :"" }
</div>
</LongWidth>
{

View File

@ -9,6 +9,11 @@ const typeIco = {
}
function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId , platform }){
//
function getMessage(value){
let str = value.indexOf('\n') > 0 && value.split('\n');
return str[0];
}
return(
<li>
<span>
@ -16,14 +21,14 @@ function CoderDepotCatalogue({item , goToSubRoot , owner , projectsId , platform
(!platform && item.image_type) ?
<span><i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}</span>
:
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)} className={item.type === "submodule" && "submoduleStyle"}>
<a onClick={()=>goToSubRoot(item.path,item.type,item.name)} className={item.type === "submodule" ? "submoduleStyle":''}>
<i className={`iconfont ${typeIco[`${item.type}`]} mr8`}></i>{item.name}
</a>
}
</span>
<span title="init project">
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(`${item.commit && item.commit.sha}`)}`} title={item.commit && item.commit.message}>
{item.commit && item.commit.message}
{getMessage(item.commit && item.commit.message)}
</Link>
</span>
<span title={item.commit && item.commit.created_at}>{item.commit && item.commit.time_from_now}</span>

View File

@ -1,5 +1,5 @@
import { result } from 'lodash';
import React from 'react';
import { returnbar } from 'educoder';
function CoderDepotPath({treeValuePath , returnUlr , returnMain , getPathUrl , identifier}){
@ -14,12 +14,12 @@ function CoderDepotPath({treeValuePath , returnUlr , returnMain , getPathUrl , i
</a>
{treeValuePath.map((item, key) => {
return (
<React.Fragment>
<React.Fragment key={key}>
{
key === treeValuePath.length-1 ?
<span className="color-grey-6 subFileName" key={key}>{item}</span>
<span className="color-grey-6 subFileName" key={key}>{returnbar(item)}</span>
:
<a onClick={()=>returnUlr(`${getPathUrl(treeValuePath,key+1)}`)} className="color-blue subFileName">{item}</a>
<a onClick={()=>returnUlr(`${getPathUrl(treeValuePath,key+1)}`)} className="color-blue subFileName">{returnbar(item)}</a>
}
</React.Fragment>
);

View File

@ -4,6 +4,7 @@ import { Link, Route, Switch } from 'react-router-dom';
import { Content, AlignTop } from '../Component/layout';
import DetailBanner from './sub/DetailBanner';
import cookie from 'react-cookies';
import { Base64 } from 'js-base64';
import '../css/index.scss'
import './list.scss';
@ -145,6 +146,14 @@ const WikiEdit = Loadable({
loader: () => import('../Wiki/EditWiki'),
loading: Loading,
});
const Invite = Loadable({
loader: () => import('../Invite/Index'),
loading: Loading,
});
const Review = Loadable({
loader: () => import('../Newfile/codeReview'),
loading: Loading,
});
/**
* permissionManager:管理员Reporter报告人员(只有读取权限)Developer开发人员除不能设置仓库信息外
*/
@ -227,12 +236,12 @@ class Detail extends Component {
})
}
clearIssueCookies=(history)=>{
clearIssueCookies = (history) => {
const { pathname } = history;
const { projectsId , owner } = this.props.match.params;
const { projectsId, owner } = this.props.match.params;
let currentIssue = pathname.indexOf(`/${owner}/${projectsId}/issues`) === -1;
if (currentIssue) {
cookie.save('states', undefined,{ expires: 0,path:`/` });
cookie.save('states', undefined, { expires: 0, path: `/` });
}
}
@ -304,15 +313,15 @@ class Detail extends Component {
},
disconnected: () => {
console.log("###### cannot connected! ######");
},
},
received: data => {
console.log(`###### ---received data--- ######`);
console.log(data);
if (data) {
if(deleteFlag){
if (deleteFlag) {
this.props.showNotification("镜像同步成功!");
window.location.reload();
}else{
} else {
if (data.project && data.project.mirror_status === 2) {
this.deleteProjectBack();
}
@ -326,20 +335,20 @@ class Detail extends Component {
cable.subscriptions.consumer.disconnect();
}
},
onerror:()=>{
onerror: () => {
console.log("###### cannot connected! ######");
}
});
this.timerChannel = setTimeout(this.reloadDetail,5000);
this.timerChannel = setTimeout(this.reloadDetail, 5000);
}
reloadDetail=()=>{
if(this.state.firstSync||this.state.secondSync){
reloadDetail = () => {
if (this.state.firstSync || this.state.secondSync) {
window.location.reload();
}
}
deleteProjectBack = () => {
const { history } = this.props;
@ -364,22 +373,33 @@ class Detail extends Component {
axios.get(url).then((result) => {
if (result && result.data) {
if (result.data.status === 404) {
this.props.history.push('/nopage');
if (window.location.pathname.includes('/invite')) {
let inviteString = window.location.search && window.location.search.split('?invite=')[1];
let data = inviteString && JSON.parse(Base64.decode(inviteString));
const { project = {}, projectDetail = {} } = this.state
this.setState({
project: Object.assign(project, { author: { name: data.ownerName } }),
projectDetail: Object.assign(projectDetail, { name: data.projectName })
});
} else {
this.props.history.push('/nopage');
}
} else {
this.setState({
projectDetail: result.data,
project_id: result.data.project_id,
isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"),
isReporter: result.data.permission && result.data.permission === "Reporter",
isDeveloper: result.data.permission && result.data.permission === "Developer",
http_url: result.data.clone_url,
praised: result.data.praised,
watched: result.data.watched,
watchers_count: result.data.watchers_count,
praises_count: result.data.praises_count,
forked_count: result.data.forked_count,
defaultBranch: result.data.default_branch
})
}
this.setState({
projectDetail: result.data,
project_id: result.data.project_id,
isManager: result.data.permission && (result.data.permission === "Manager" || result.data.permission === "Admin" || result.data.permission === "Owner"),
isReporter: result.data.permission && result.data.permission === "Reporter",
isDeveloper: result.data.permission && result.data.permission === "Developer",
http_url: result.data.clone_url,
praised: result.data.praised,
watched: result.data.watched,
watchers_count: result.data.watchers_count,
praises_count: result.data.praises_count,
forked_count: result.data.forked_count,
defaultBranch: result.data.default_branch
})
}
}).catch((error) => { })
}
@ -464,7 +484,7 @@ class Detail extends Component {
const url = `/${owner}/${projectsId}/forks.json`;
axios.post(url).then(result => {
if (result && result.data.status === 0) {
if(result.data.message === "fork失败你已拥有了这个项目"){
if (result.data.message === "fork失败你已拥有了这个项目") {
this.props.history.push(`/${current_user && current_user.login}/${projectsId}`);
return;
}
@ -490,7 +510,7 @@ class Detail extends Component {
axios.post(url).then(result => {
if (result && result.data && result.data.status === 0) {
this.setState({
secondSync:true
secondSync: true
})
this.canvasChannel(true);
} else {
@ -527,10 +547,9 @@ class Detail extends Component {
let pathname = checkPathname(projectsId, owner, url);
const { state } = this.props.history.location;
const common = {
getDetail: this.getDetail,
getBanner:this.getBanner,
getBanner: this.getBanner,
changeOpenDevops: this.changeOpenDevops,
defaultBranch
}
@ -549,7 +568,7 @@ class Detail extends Component {
<Link to={`/${owner}/${projectsId}`} className="projectN mt6">{projectDetail && projectDetail.name}</Link>
</div>
{projectDetail && projectDetail.private && <span className="privateTag mt6">私有</span>}
{ !platform && <span className="privateTag red mt6">只读</span> }
{!platform && <span className="privateTag red mt6">只读</span>}
</AlignTop>
<div className="mt8">
{
@ -644,6 +663,8 @@ class Detail extends Component {
open_devops={open_devops}
platform={platform}
urlFlag={urlFlag}
showNotification={this.props.showNotification}
current_user={current_user}
isManager={isManager}
/>
}
@ -687,6 +708,12 @@ class Detail extends Component {
() => (<WikiEdit {...this.props} {...this.state} {...common} />)
}
></Route>
{/* wiki具体某一个地址 */}
<Route path="/:owner/:projectsId/wiki/:wikiName"
render={
(props) => (<Wiki {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* wiki */}
<Route path="/:owner/:projectsId/wiki"
render={
@ -711,7 +738,7 @@ class Detail extends Component {
(props) => (<Setting {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/*修改里程碑*/}
<Route path="/:owner/:projectsId/milestones/:meilid/edit"
render={
@ -751,18 +778,18 @@ class Detail extends Component {
{/* 修改详情 edit*/}
<Route path="/:owner/:projectsId/issues/:orderId/edit"
render={
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"edit"}/>)
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"edit"} />)
}
></Route>
{/* 复制详情 copyetail*/}
<Route path="/:owner/:projectsId/issues/:orderId/copyetail"
render={
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"copy"}/>)
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common} form_type={"copy"} />)
}
></Route>
{/* 任务详情 */}
<Route path="/:owner/:projectsId/issues/:orderId"
{/* 任务详情 */}
<Route path="/:owner/:projectsId/issues/:orderId"
render={
(props) => (<OrderDetail {...this.props} {...this.state} {...props} {...common} />)
}
@ -795,6 +822,12 @@ class Detail extends Component {
(props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* review */}
<Route path="/:owner/:projectsId/pulls/:mergeId/review"
render={
(props) => (<Review {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId"
render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
@ -854,6 +887,13 @@ class Detail extends Component {
(props) => (<CoderDepot {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
{/* 邀请 */}
<Route path="/:owner/:projectsId/invite"
render={
(props) => (<Invite {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/:subIndex"
render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common} />)

View File

@ -122,7 +122,7 @@
max-width: 1200px;
margin: 0 auto;
.panelmenu{
padding-top:30px;
padding-top:20px;
.depotBtn{
.mr-5{
margin-right: -5px;
@ -334,6 +334,14 @@
.listtablebody{
border-radius:0px 0px 4px 4px ;
border: 1px solid #D0D0D0;
li{
a{
color: #05101a;
&:hover{
color:$primary-color;
}
}
}
li.listtablepath{
a{color: #40a9ff;}
p{

View File

@ -306,6 +306,14 @@
}
.files-md{
padding:20px;
h1,h2, h3, h4, h5, h6{
margin-bottom: .5em !important;
}
}
.readBox{
h1,h2, h3, h4, h5, h6{
margin-bottom: .5em !important;
}
}
/* 详情-代码 */
.branch-wrapper{
@ -512,7 +520,7 @@
top: 0px;
color: #999;
height: 22px;
line-height: 22px;
line-height: 17px;
}
.addFile{
display: flex;

View File

@ -2,11 +2,14 @@ import React, { useEffect, useState } from 'react';
import { Skeleton , Tooltip} from 'antd';
import { Link } from 'react-router-dom';
import { numFormat } from 'educoder';
import QuitBox from './quit';
import axios from 'axios';
function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){
function DetailBanner({ history,list , owner , projectsId ,showNotification , url , pathname , state , urlFlag , projectDetail , platform ,open_devops ,current_user, isManager }){
const [ menuName , setMenuName ] = useState(undefined);
const [ visible , setVisible ] = useState(false);
useEffect(()=>{
if(list){
if(Array.isArray(list)){
// banner
let a = list.filter(item=>item.menu_name === "resources");
if((pathname && pathname==="source") && (a && a.length === 0)){
@ -19,13 +22,25 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
setMenuName(list);
}
},[list]);
function onSuccess(){
const url = `/${owner}/${projectsId}/quit.json`;
axios.post(url).then(result=>{
if(result){
showNotification("已成功退出仓库!");
history.push(`/${current_user && current_user.login}`);
}
}).catch(error=>{})
}
return(
<div className="f-wrap-between mt25">
<QuitBox visible={visible} onCancel={()=>setVisible(false)} name={projectDetail && projectDetail.name} onSuccess={onSuccess}/>
{
menuName && projectDetail ?
menuName && projectDetail ?
<ul className="headerMenu-wrapper">
{
Array.isArray(menuName)&& menuName.map((item,key)=>{
Array.isArray(menuName)&& menuName.map((item,key)=>{
return(
<React.Fragment key={item.menu_name}>
{
@ -68,8 +83,9 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
</Link>
</li>:""
}
{/* 引擎仅对当前仓库管理员可见 */}
{
item.menu_name === "devops" ?
item.menu_name === "devops" ?
<li className={pathname==="devops" ? "active" : ""}>
{/* <Link to={{ pathname: `/${owner}/${projectsId}/devops${open_devops ? `/dispose`:""}`, state }}> */}
<Link to={{ pathname: `/${owner}/${projectsId}/devops`, state:{...state,open_devops} }}>
@ -135,6 +151,10 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
</Link>
</li>
}
{
item.menu_name === "quit" &&
<a onClick={()=>setVisible(true)}><i className="iconfont icon-tuichuicon color-grey-3 mr5 font-14"></i>退出仓库</a>
}
</React.Fragment>
)
})

View File

@ -0,0 +1,27 @@
import React from 'react';
import { AlignCenter } from '../../Component/layout';
import Modals from '../../Component/PublicModal/Index';
import { Button } from 'antd';
function QuitBox({ visible , onCancel ,onSuccess , name }) {
return(
<Modals
visible={visible}
onCancel={onCancel}
title={"退出仓库"}
btn={
<div>
<Button size={'large'} onClick={onCancel}>取消</Button>
<Button type={"danger"} size={"large"} onClick={onSuccess}>确定</Button>
</div>
}
>
<div className="desc">
<AlignCenter className="descMain">
<i className="iconfont icon-jinggao1 mr10 font-20 red"></i>确定退出 {name} 仓库吗</AlignCenter>
<p style={{textAlign:"center"}}>请进行确认以防数据的丢失</p>
</div>
</Modals>
)
}
export default QuitBox;

View File

@ -6,7 +6,7 @@ import MergeFooter from './merge_footer';
import { returnbar , turnbar } from 'educoder';
import { Base64 } from 'js-base64';
import '../Order/order.css';
import '../Order/order.scss';
import './merge.css';
/**

View File

@ -1,6 +1,6 @@
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Tag } from "antd";
import { Tag, Tooltip } from "antd";
import { AlignCenter } from '../Component/layout';
import { getImageUrl, turnbar } from "educoder";
import "./merge.css";
@ -157,7 +157,7 @@ class MergeItem extends Component {
"--"
)}
</li>
<li>{item.version || "--"}</li>
<li>{item.version ? <Tooltip placement="topLeft" title={item.version}>{item.version}</Tooltip> : "--"}</li>
<li>
<div

View File

@ -7,7 +7,7 @@ import Commits from './Commits';
import Comments from '../comments/comments';
import Files from './Files';
import '../Order/order.css';
import '../Order/order.scss';
import './merge.css';
const { TabPane } = Tabs;

View File

@ -16,7 +16,7 @@ import {
} from "antd";
import "./merge.css";
import RenderHtml from "../../components/render-html";
import "../Order/order.css";
import "../Order/order.scss";
import MergeLinkFooter from "./MergeLinkFooter";
const TextArea = Input.TextArea;
@ -293,6 +293,12 @@ class MessageCount extends Component {
)
}
codeReview=()=>{
const {history,match}=this.props;
const { projectsId, mergeId , owner } =match.params;
history.push(`/${owner}/${projectsId}/pulls/${mergeId}/review`);
}
render() {
const { projectsId, mergeId , owner } = this.props.match.params;
@ -449,6 +455,18 @@ class MessageCount extends Component {
编辑
</Button>
)}
{operate && (
<Button
type="blue"
ghost
className="ml20"
onClick={this.codeReview}
>
代码评审
</Button>
)}
{operate && (
<Button
type="danger"

View File

@ -1,7 +1,7 @@
import React, { Component } from "react";
import { Input, Select , Spin, Alert } from "antd";
import axios from "axios";
import "../Order/order.css";
import "../Order/order.scss";
import "./merge.css";
import MergeForm from "./merge_form";
import MergeFooter from "./merge_footer";

View File

@ -1,7 +1,7 @@
import React, { Component } from "react";
import { Input, Select, Button, Spin, Empty } from "antd";
import axios from "axios";
import "../Order/order.css";
import "../Order/order.scss";
import "./merge.css";
import MergeForm from "./merge_form";
import MergeFooter from "./merge_footer";

View File

@ -117,6 +117,11 @@ form .ant-cascader-picker, form .ant-select {
border:1px solid #28BD6C;
color: #28BD6C;
}
/* 绿色按钮-type="blue" */
.ant-btn.ant-btn-blue{
border:1px solid #466aff;
color: #466aff;
}
.copyTab{
min-width: 370px;
padding:0px 18px 22px 18px;

View File

@ -1,7 +1,7 @@
import React, { Component } from "react";
import { Input, Dropdown, Menu, Icon, Pagination, Spin } from "antd";
import "./merge.css";
import "../Order/order.css";
import "../Order/order.scss";
import "../Order/index.scss";
import NoneData from "./no_data";
import MergeItem from "./MergeItem";
@ -247,7 +247,7 @@ class merge extends Component {
</Menu>
);
return (
<div className="main" style={{padding:"0px"}}>
<div className="main mt20" style={{padding:"0px"}}>
<div className="topWrapper" style={{borderBottom:"none",padding:"20px"}}>
<div className="target-detail-search">
<Search

View File

@ -3,7 +3,7 @@ import { Tabs } from 'antd';
import Commits from './Commits';
import Files from './Files';
import '../Order/order.css';
import '../Order/order.scss';
import './merge.css';
const { TabPane } = Tabs;

View File

@ -2,7 +2,7 @@ import React, { Component } from "react";
import { Button, Form, Menu, Input, Select, Tag, Checkbox, Spin } from "antd";
import axios from "axios";
import "../Order/order.css";
import "../Order/order.scss";
import "./merge.css";
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";

View File

@ -0,0 +1,31 @@
const IPluginAPI = {
commands: {}
}
export class Plugin {
PLUGIN_ID = 'ChangeThemePlugin';
_commands = IPluginAPI['commands'] || null;
get commands() {
return this._commands;
}
activate({ context, commands }) {
this._commands = commands;
context.subscriptions.push(
commands.registerCommand(
'ChangeThemePlugin.changeTheme',
(value) => {
commands.executeCommand(
'alex.setDefaultPreference',
'general.theme',
value
);
}
)
);
}
}
export default new Plugin();

View File

@ -0,0 +1,283 @@
import React, { Fragment, useState, useMemo, useEffect, useRef } from 'react';
import { Switch ,Checkbox ,Tooltip,Popover,Radio,Icon} from 'antd';
import { EditorRenderer } from 'alex';
import { useLocalStorageState } from 'ahooks';
import html from './extensions/alex-ext-public.html-language-features-worker.js';
import css from './extensions/alex-ext-public.css-language-features-worker.js';
import json from './extensions/alex-ext-public.typescript-language-features-worker.js';
import typescript from './extensions/alex-ext-public.json-language-features-worker.js';
import markdown from './extensions/alex-ext-public.markdown-language-features-worker.js';
import ideTheme from './extensions/alex-ext-public.ide-dark-theme.js';
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
import { getUrl, returnbar } from 'educoder';
import changeThemePlugin from './changeTheme';
import IDEPlugin, { ExtensionCommand } from './ide-plugin';
// import { FileOperationArea } from './components/file-operation-area';
import { repoService } from './codeReview/mock/repo.service';
function CloudIDE({ download_url, params: { owner, projectsId, branchName }, filepath }) {
function FileContentStore() {
const [loading, setLoading] = useState(true);
const [showIde, setShowIde] = useState(false);
const [fileSize, setFileSize] = useState('0');
const [fileName, setFileName] = useState('');
return {
loading,
setLoading,
showIde,
setShowIde,
fileSize,
setFileSize,
fileName,
setFileName,
};
}
const { fileSize, fileName } =
FileContentStore();
const fileCache = new Map();
const copyFile = () => {
console.log('复制文件内容');
};
const [settingTipVisible, setSettingTipVisible] = useLocalStorageState(
'code.file.operation.tip',
true
);
const [lines, setLines] = useState(0);
const [sloc, setSloc] = useState(0);
const [encoding, setEncoding] = useState('UTF-8');
// const [showMdPreview, setShowPreview] = useState(false);
const [fileString, setFileString] = useState('');
const curFilepath = useMemo(() => {
// pathreadme
//
// const needLoadFile = treeEntry
// ? treeEntry.render === FileShowType.text &&
// treeEntry.size < ReadFileSizeLimit
// : !!fileName;
// return needLoadFile ? path || fileName : '';
}, [branchName, fileName]);
const [blameChecked, setBlameChecked] = useState(false);
//
// useUnmount(() => {
// fileCache.clear()
// })
// editor readFile
//
// editor api
useEffect(() => {
setBlameChecked(false);
const fileKey = `${branchName}-${curFilepath}`;
const fileInfo = fileCache.get(fileKey);
if (fileInfo) {
setFileString(fileInfo.fileString);
setLines(fileInfo.lines);
setSloc(fileInfo.sloc);
}
}, [curFilepath]);
//
const [pluginActivated, setPluginActivated] = useState(false);
const plugin = useRef(
new IDEPlugin(
() => setPluginActivated(true),
(commitId) => window.open(`/${owner}/${projectsId}/commits/${commitId}`)
)
);
// blame
const onBlame = async (e) => {
setBlameChecked(e.target.checked);
console.log('onBlame:', e.target.checked);
if (!e.target.checked) {
plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame);
} else {
const res = await repoService.getCodeBlame(projectsId, owner, { sha: branchName, filepath })
plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame, res);
}
};
const [wordWrap, setWordWrap] = useState(false);
const onWordWrapChange = (value) => {
const preference = value ? 'on' : 'off';
localStorage.setItem('code.file.viewer.worlWrap', preference);
plugin.current.setPerference('editor.wordWrap', preference, true);
setWordWrap(value);
};
async function changeStyle(v) {
const commands = changeThemePlugin.commands;
if (commands) {
await commands.executeCommand(
'ChangeThemePlugin.changeTheme',
v ? 'opensumi-light' : 'opensumi-dark'
);
}
}
const content = (
<Fragment>
<div className="flex align-center">
<span className="c-65">{'编码方式'}</span>
<span>
<Radio.Group
onChange={(e) => {
setEncoding(e.target.value);
}}
value={encoding}
size="small"
>
<Radio value="UTF-8">UTF-8</Radio>
<Radio value="GBK">GBK</Radio>
</Radio.Group>
</span>
</div>
<div className="flex align-center">
<span className="c-65">{'自动换行'}</span>
<span>
<Radio.Group
onChange={(e) => {
onWordWrapChange(e.target.value);
}}
value={wordWrap}
size="small"
>
<Radio value={false}>{'不自动换行'}</Radio>
<Radio value={true}>{'自动换行'}</Radio>
</Radio.Group>
</span>
</div>
</Fragment>
);
return (
<Fragment>
<div className="ide-tool-bar">
{onBlame ? (
<Checkbox onChange={onBlame} checked={!!blameChecked}>
Blame
</Checkbox>
) : null}
<Switch checkedChildren="light" unCheckedChildren="dark" defaultChecked onChange={changeStyle}></Switch>
<Tooltip
title={'更改阅读设置'}
defaultVisible={settingTipVisible}
onVisibleChange={setSettingTipVisible}
>
<Popover
content={content}
title={null}
trigger="click"
placement="bottomRight"
arrowPointAtCenter={true}
>
{/* <Icon type="ellipsis" /> */}
<Icon className="read-more" type="read" />
</Popover>
</Tooltip>
</div>
{/* <FileOperationArea
lines={lines}
sloc={sloc}
size={fileSize}
refName={branchName}
path={curFilepath}
encoding={encoding}
setEncoding={setEncoding}
onFileCopy={copyFile}
blameChecked={blameChecked}
onBlame={pluginActivated && onBlame}
wordWrap={wordWrap}
setWordWrap={onWordWrapChange}
/> */}
<EditorRenderer
appConfig={{
//
plugins: [changeThemePlugin, plugin.current],
workspaceDir: `${owner}/${projectsId}`,
defaultPreferences: {
// opensumi-dark
// 'general.theme': 'opensumi-light',
'general.theme': 'opensumi-light',
//
'editor.forceReadOnly': true,
//
'editor.scrollBeyondLastLine': false,
},
extensionMetadata: [
html,
css,
json,
typescript,
markdown,
ideTheme,
CodeBlame],
}}
runtimeConfig={{
//
biz: 'gitlink',
// editor null
scenario: null,
// editor none
startupEditor: 'none',
// editor tab
hideEditorTab: true,
}}
editorConfig={{
stretchHeight: true,
disableEditorSearch: true,
}}
//
documentModel={{
// code code
type: 'code',
// tag
ref: branchName,
//
owner: owner,
//
name: projectsId,
//
filepath: filepath,
//
readFile: async (filepath) => {
if (!filepath) {
return;
}
//
// const res = await fetch('https://gist.githubusercontent.com/gaearon/0b180827c190fe4fd98b4c7f570ea4a8/raw/b9157ce933c79a4559d2aa9ff3372668cce48de7/LikeButton.js');
const res = await fetch(`${getUrl()}/attachments/entries/get_file?download_url=${download_url}`);
setTimeout(()=>{
onWordWrapChange(localStorage.getItem('code.file.viewer.worlWrap') === 'on');
},5000)
return res.arrayBuffer();
},
// utf8 gbk
// encoding: 'utf8',
encoding,
onFilepathChange: (changedFilepath) => {
if (changedFilepath) {
console.log(`路由跳转到 ${changedFilepath}`)
}
},
//
// lineNumber: [0, 1],
onLineNumberChange: line => {
if (typeof line === 'number') {
console.log(`#L${line}`)
} else {
console.log(`#L${line[0]}-${line[1]}`)
}
},
}}
/>
</Fragment>
)
}
export default CloudIDE;

View File

@ -0,0 +1,56 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'editor-plugin-blame',
version: '0.2.6',
},
packageJSON: {
name: 'editor-plugin-blame',
publisher: 'alex-ext-public',
version: '0.2.6',
repository: {
type: 'git',
url: 'http://code.alipay.com/yxy167584/editor-plugin-blame.git',
},
displayName: 'editor-plugin-blame',
description: ' ',
activationEvents: ['*'],
kaitianContributes: {
workerMain: './out/worker/index.js',
},
contributes: {
commands: [
{
command: 'code.blame.toggleBlame',
title: '查看blame',
},
{
command: 'code.blame.acrToggleBlame',
title: 'blame',
},
{
command: 'code.blame.linktocommit',
title: 'hover详情跳转',
},
],
views: {},
menus: {
'editor/title': [
{
command: 'code.blame.acrToggleBlame',
type: 'checkbox',
group: 'navigation',
toggledWhen: 'acr_blame_context',
when: 'resourceScheme =~ /^git$|^diff$/',
},
],
},
workerMain: './out/worker/index.js',
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: ['package.json', 'out/worker/index.js'],
mode: 'public',
};

View File

@ -0,0 +1,72 @@
import { DependencyList, useEffect, useState, useMemo } from 'react';
// @ts-ignore
import { useEventEmitter, usePrevious } from 'ahooks';
// @ts-ignore
import sha1 from 'sha1';
// @ts-ignore
import differenceBy from 'lodash/differenceBy';
export function useRequest(
factory,
options
) {
const { deps = [], initial, ready = true } = options || {};
const [val, setVal] = useState(initial);
useEffect(() => {
if (!ready) return;
let cancel = false;
const promise = factory();
if (promise === undefined || promise === null) return;
promise
.then((val) => {
if (!cancel) {
setVal(val);
}
})
.catch((err) => {
console.error(err);
});
return () => {
cancel = true;
};
}, [...deps, ready]);
return val;
}
export function useFileReadMarkChange$(
diffs,
readMarks
) {
const fileReadMarkChange$ = useEventEmitter();
const filePathShaMap = useMemo(() => {
const map = new Map();
for (const diff of diffs) {
map.set(sha1(diff.newPath), diff.newPath);
}
return map;
}, [diffs]);
const prevReadMarks = usePrevious(readMarks);
useEffect(() => {
const changesA = differenceBy(
prevReadMarks,
readMarks,
(readMark) => readMark.filePathSha
);
const changesB = differenceBy(
readMarks,
prevReadMarks,
(readMark) => readMark.filePathSha
);
for (const change of [...changesA, ...changesB]) {
const filePath = filePathShaMap.get(change.filePathSha);
if (filePath) {
fileReadMarkChange$.emit(filePath);
}
}
}, [readMarks, filePathShaMap]);
return fileReadMarkChange$;
}

View File

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 200 200">
<defs>
<linearGradient id="linear-gradient" y1="0.5" x2="1" y2="0.5" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#6dffff"/>
<stop offset="1" stop-color="#0080ff"/>
</linearGradient>
</defs>
<g id="组_1758" data-name="组 1758" transform="translate(9483 -10311)">
<rect id="矩形_340" data-name="矩形 340" width="200" height="200" transform="translate(-9483 10311)" fill="#fff" opacity="0"/>
<g id="图层_2" data-name="图层 2" transform="translate(-9472 10328)">
<g id="图层_1" data-name="图层 1">
<path id="路径_1227" data-name="路径 1227" d="M127.508,32.458,123.06,26.4l-3.9,1.958V77.007l3.239,1.7v-5.4l5.11,8.248,4.145.648V23.03l-4.145,1.6ZM35.847,82.2l9.284-4.62V27.708L35.847,23.03Zm63.463,6.29,8.305,4.951V9.241L99.31,14.192ZM87.378,0H75.863V105.291H93.509V94.841H87.378ZM49.954,21.821l4.577,1.684V93.344l8.348-4.318V27.132l4.477,1.655V19.3L49.954,9.226ZM5.448,78.634,25.6,68.457V49.3l-9.543-1.022v6.736h3.829V61.52l-6.621,3.008V43.873L25.541,46.06V38.216L5.39,31.58ZM154.18,54.855,168.919,31.58l-11.04,1.195-7.355,9.788V36.071L142.406,38V73.006l8.118,1.684V67.982l7.845,10.076,12.7.633Z" transform="translate(2.368)" fill-rule="evenodd" fill="url(#linear-gradient)"/>
<path id="路径_1228" data-name="路径 1228" d="M0,110.683a27.2,27.2,0,0,0,2.634-4.577q1.051-2.375,1.727-4.4a33.61,33.61,0,0,0,.979-3.527c.2-.993.317-1.612.345-1.9V92.964h-3.5V88.876H17.445v4.088H9.37V96.75q0,.158-.432,2.245a34.545,34.545,0,0,1-1.439,5.11h5.556v-.72H16.74V122.86a4.822,4.822,0,0,1-1.209,3.325,3.915,3.915,0,0,1-3.008,1.339h-8.2V110.942l-.72,1.137c-.23.389-.489.763-.763,1.152Zm8.017,12.753h4.491c.36,0,.547-.187.547-.576V108.193H8.017Zm9.471-27.521q1.209-1.6,2.073-2.879c.576-.82,1.195-1.77,1.842-2.879a28.457,28.457,0,0,0,1.727-3.267L26.4,88.7a17.271,17.271,0,0,1-.849,1.713H39.727V93.67l-3.354,4.995h2v-.777h3.685v24.758a4.808,4.808,0,0,1-1.209,3.325,3.872,3.872,0,0,1-2.98,1.353h-2.1v-4.045h2.058c.374,0,.547-.2.547-.619v-3.368h-6.29v7.2H28.4v-7.2H22.3q-.23,1.67-.518,3.109c-.187.964-.36,1.814-.533,2.562s-.317,1.353-.446,1.814a7.658,7.658,0,0,0-.187.748l-1.77-.619-1.727-.619c.4-1.6.734-3.008.979-4.318s.475-2.648.677-4.189a35.307,35.307,0,0,0,.3-4.592V98.664H31.811l2.735-4.088H23.131q-1.123,1.756-1.986,2.879l-.993,1.108ZM28.4,115.188v-4.707H22.757v4.707Zm-5.642-12.436v3.627H28.4v-3.627Zm15.617,0h-6.29v3.627h6.29Zm0,12.436v-4.707h-6.29v4.707Z" transform="translate(0 38.179)" fill="#333"/>
<path id="路径_1229" data-name="路径 1229" d="M32.82,110.943H51.014c.518-.806.993-1.612,1.439-2.447a15.2,15.2,0,0,0,1.036-2.634V97.168h3.685V106.7a21.591,21.591,0,0,1-1.7,4.2H73.382v4.088H58.427l14.509,8.751L71.151,127.4,55.49,117.91l1.439-2.879H52.756a31.018,31.018,0,0,1-3.742,3.786,41.975,41.975,0,0,1-4.045,3.051,43.833,43.833,0,0,1-3.944,2.361c-1.267.677-2.418,1.223-3.426,1.655s-1.842.777-2.49,1.008l-1.123.417-.518-1.972-.5-1.914s.5-.173,1.439-.547,2.116-.878,3.527-1.569,2.965-1.555,4.635-2.591a32.934,32.934,0,0,0,4.908-3.685H32.82Zm.1-19.849H51.287l-1.166-2.433,3.267-1.871,2,4.318H69.251v-.734h3.685V99.86H69.251V95.182H36.606v4.865H32.921Zm4.75,10.824,9.889,4.462-1.353,3.742-9.889-4.462Zm12.681,3.325-9.831-4.62,1.439-3.786,9.831,4.649Z" transform="translate(14.421 38.135)" fill="#333"/>
<path id="路径_1230" data-name="路径 1230" d="M105.835,108.223h-9.37v18.453h-3.7V108.223H77.307a38.49,38.49,0,0,1-2.03,7.125,62.419,62.419,0,0,1-2.634,5.758,44.3,44.3,0,0,1-2.332,3.944c-.691.979-1.065,1.526-1.123,1.626l-2.879-2.591.907-1.3a34.806,34.806,0,0,0,2.015-3.4c.763-1.439,1.54-3.109,2.346-5.023a34.877,34.877,0,0,0,1.958-6.146H64.05v-4.088H74.126V92.117H66.857V88h36.229v4.1H96.465v12.019h9.37Zm-13.07-4.088V92.117H77.955v12.019Z" transform="translate(28.143 38.666)" fill="#333"/>
<path id="路径_1231" data-name="路径 1231" d="M98.847,127.257l-3.267-1.77,6.348-14.394,3.311,1.814Zm3.958-29.8L96,90.667l2.433-3.008,6.852,6.794Zm0,10.882L96,101.549l2.433-3.008,6.852,6.794Zm.187,16.985c.317-.59.72-1.439,1.238-2.533a29.74,29.74,0,0,0,1.439-3.6,34.757,34.757,0,0,0,1.281-3.987,14.767,14.767,0,0,0,.547-3.714V87.4h28.788v4.1H111.255V111.5a17.965,17.965,0,0,1-.36,3.4,30.226,30.226,0,0,1-.921,3.527c-.374,1.152-.777,2.26-1.209,3.311s-.82,2-1.195,2.879-.691,1.439-.964,2l-.446.849Zm7.931-.1a34.386,34.386,0,0,0,2.116-4.059c.576-1.339,1.051-2.519,1.439-3.555a30.893,30.893,0,0,0,1.022-3.527l3.6.993s-.1.461-.288,1.18-.475,1.67-.878,2.879-.921,2.461-1.569,3.93a35.5,35.5,0,0,1-2.346,4.491Zm13.617-31.451-1.022,1.871h7.787v-.936h3.742v13.7a4.836,4.836,0,0,1-1.209,3.3,3.858,3.858,0,0,1-2.994,1.439H126.1v9.586a4.736,4.736,0,0,1-1.223,3.282,3.8,3.8,0,0,1-2.98,1.439h-2.749v-4.1h2.706a.576.576,0,0,0,.4-.144.6.6,0,0,0,.158-.417v-9.586h-8.953V95.648h5.758L121.46,91.6Zm6.765,6H117.128v2.792h14.178Zm-.518,9.27q.518,0,.518-.561v-1.814H117.128v2.375ZM132,113.827l4.433,11.8-3.4,1.5-4.318-11.8Z" transform="translate(41.997 38.403)" fill="#333"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,292 @@
import React, { useEffect, useState, useMemo } from 'react';
// import { default as AntcodeCR } from 'acr';
import { ACR } from 'alex';
import { Button, Switch, Spin, Icon } from 'antd';
// blame worker
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
// plugin
import CodeBlamePlugin, { ExtensionCommand } from './plugins/code-blame.plugin';
import { usePersistFn } from "ahooks";
import {windowsOrMac} from 'educoder';
import {
Provider,
useGlobal,
usePr,
useNote,
useReadMark,
useAcr,
useSetting,
} from './model';
import {
DiscussionItem,
Commenting,
Menubar,
AnnotationEntry,
PRMoreActionLinks,
} from './mock/component/index';
import { projectService } from './mock/project.service';
import { lsifService } from './mock/lsif.service';
import { useFileReadMarkChange$ } from './hooks';
import { repoService } from './mock/repo.service';
import ideLogo from './ideLogo.svg';
const CodeReview = (props) => {
const { match: { params: { owner, projectsId, mergeId } }, projectDetail, current_user } = props;
const [visible, setVisible] = React.useState(true);
const [count, setCount] = React.useState(0);
const [isFullscreen, setFullscreen] = React.useState(false);
const { locale, setLocale, gbk, setGBK } = useSetting();
const { commentPack } = useNote();
const { project, user, setCurrent_user, setProjectDetail } = useGlobal();
const { pr } = usePr();
const {
getFileReadStatus: _getFileReadStatus,
markFileAsRead,
markFileAsUnread,
readMarks,
} = useReadMark();
// todo
const getFileReadStatus = usePersistFn(_getFileReadStatus);
// const getFileReadStatus = _getFileReadStatus;
const {
diffsPack,
getDiffById,
getFileContent,
IDEMode,
toggleViewerType,
annotationPacks,
setAcrFlag,
} = useAcr();
const fileReadMarkChange$ = useFileReadMarkChange$(
diffsPack && diffsPack.diffs || [],
readMarks
);
//
const [pluginActivated, setPluginActivated] = useState(false);
const blamePlugin = useMemo(() => {
return new CodeBlamePlugin(
() => setPluginActivated(true),
(commitId) => {
window.open(
`/${owner}/${projectsId}/commit/${commitId}`
)
},
// blame
(projectId, commitId, filepath) =>
repoService.getCodeBlame(projectsId, owner, { sha: commitId, filepath })
);
}, []);
useEffect(() => {
if (!pluginActivated) {
return;
}
const projectData = {
projectId: projectsId,
prevSha: diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
nextSha: diffsPack.toVersion.headCommitSha,
};
blamePlugin.commands && blamePlugin.commands.executeCommand(
ExtensionCommand.setProjectData,
projectData
);
//
blamePlugin.commands && blamePlugin.commands.executeCommand(
'alex.setDefaultPreference',
'acr.lsifEnabled',
false
);
}, [pluginActivated, diffsPack]);
if (!diffsPack) {
return <Spin style={{ height: "100vh" }} spinning={true}></Spin>
}
function EditorEmpty() {
const system=windowsOrMac();
console.log(system);
return <div className='ide-logo'>
<img className='ide-logo-img' src={ideLogo} />
<div>
<div className='ide-logo-text'>IDE代码体验高效的代码编辑</div>
{/* <span className='ide-btn'>⇧</span> */}
<div className='ide-logo-text'>标记文件为已查看 <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'>C</span> </div>
<div className='ide-logo-text'>快速打开变更文件 <span className='ide-btn'>^</span> <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'>P</span></div>
<div className='ide-logo-text'>切换变更文件 <span className='ide-btn'>{system==='mac'?'⌥':'Alt'}</span> <span className='ide-btn'></span> / <span className='ide-btn'></span></div>
</div>
</div>
}
// <Icon className='ide-btn-demo' type="arrow-up" /> <Icon className='ide-btn-demo' type="arrow-down" />
const propsIDE = {
//
noteIdToReplyIdSet: commentPack.noteIdToReplyIdSet,
//
addLineNum: diffsPack.addLineNum,
//
deleteLineNum: diffsPack.delLineNum,
// sha
prevSha:
diffsPack.fromVersion && diffsPack.fromVersion.headCommitSha || diffsPack.toVersion.baseCommitSha,
// sha
nextSha: diffsPack.toVersion.headCommitSha,
toggleViewerType,
DiscussionItem,
Commenting,
getFileContent,
lineToNoteIdSet: commentPack.lineToNoteIdSet,
noteIdToNote: commentPack.noteIdToNote,
noteUpdateFlag: commentPack.updateFlag,
getDiffById,
diffs: diffsPack.diffs,
latestCommitSha: pr.diff.headCommitSha,
projectId: projectsId,
projectPath: `${owner}/${projectsId}`,
pullRequestId: mergeId,
pr,
//
getLanguages: () =>
projectService
.getLanguages(projectsId, {
aggBy: 'file_extension',
//
orderBy: 'count',
size: 20,
})
.then((res) => res && Object.keys(res)),
getFileReadStatus,
fileReadMarkChange$,
markFileAsRead,
markFileAsUnread,
bulkChangeFiles: (actions, header) => {
let files = [];
for (const item of actions) {
files.push({
action_type: item.actionType,
content: item.content,
encoding: item.encoding,
file_path: item.filePath,
});
}
let data = {
files: files,
author_email: current_user.email,
author_name: current_user.login,
committer_email: current_user.email,
committer_name: current_user.login,
branch: header.branch,
message: header.commitMessage,
};
if (pr && pr.forkProject) {
projectService.bulkChangeFiles(pr.forkProject.identifier, pr.forkProject.login, data).then(res => {
if (res) {
// giteacommit
setTimeout(() => {
setAcrFlag({});
}, 1000)
}
})
} else {
projectService.bulkChangeFiles(projectsId, owner, data).then(res => {
if (res) {
// giteacommit
setTimeout(() => {
setAcrFlag({});
}, 1000)
}
})
}
},
//
Menubar: () => (
<Menubar
initialFullscreen={isFullscreen}
handleFullscreenChange={setFullscreen}
toggleViewerType={toggleViewerType}
logFullScreen={(bool) => console.log('>>>logFullScreen', bool)}
/>
),
user,
lsifService,
defaultEncoding: project.encoding,
//
encoding: gbk ? 'gbk' : 'utf-8',
//
setEncoding: (val) => {
setGBK(val === 'gbk');
},
locale,
annotations: annotationPacks,
AnnotationEntry,
PRMoreActionLinks,
EditorEmpty: EditorEmpty,
onigWasmUri: 'https://gw.alipayobjects.com/os/lib/vscode-oniguruma/1.6.2/release/onig.wasm',
//
isFullscreen,
appConfig: {
//
plugins: [blamePlugin],
//
extensionMetadata: [CodeBlame],
},
};
const IDEContainerStyle = {
position: isFullscreen ? 'fixed' : 'static',
left: 0,
top: 0,
width: '100%',
height: isFullscreen ? '100vh' : 'calc(100vh - 72px)',
zIndex: 1002,
};
console.log('propsIDE:');
console.log(propsIDE);
return (
<div style={{ height: '100%', lineHeight: '1.4' }}>
{/* <EditorEmpty></EditorEmpty> */}
{/* <div className="controller">
{!IDEMode && (
<>
IDE 模式: <Switch checked={IDEMode} onChange={toggleViewerType} />
</>
)}
</div> */}
{/* <div className="pr-head">
<div>{pr.description}</div>
<div>
评审人
{pr.review && pr.review.reviewers && pr.review.reviewers.map((r) => (
<span style={{ marginRight: 4 }} key={r.id}>
{r.name}
</span>
))}
<span style={{ marginRight: 24 }}></span>
合并人{pr.assignee && pr.assignee.name}
</div>
</div> */}
{IDEMode && (
<div style={IDEContainerStyle}>
{visible && <ACR {...propsIDE} key={count} />}
</div>
)}
</div>
);
};
const CodeReviewP = (props) => {
return (
<Provider {...props}><CodeReview {...props} /></Provider>
)
}
export default CodeReviewP;

View File

@ -0,0 +1,5 @@
export const group = 'Gitlink';
export const repo = 'forgeplus';
export const prIid = '2';
export const project = `${group}/${repo}`;

View File

@ -0,0 +1,67 @@
import { request } from './request/index';
export const apiService = {
get(
url,
query,
options = {}
) {
return request(
url,
{
method: 'GET',
params: query,
},
options
);
},
post(
url,
query,
body,
options= {}
) {
return request(
url,
{
method: 'POST',
params: query,
data: body,
},
options
);
},
put(
url,
query,
body,
options= {}
) {
return request(
url,
{
method: 'PUT',
params: query,
data: body,
},
options
);
},
delete(
url,
query,
options = {}
) {
return request(
url,
{
method: 'DELETE',
params: query,
},
options
);
},
};

View File

@ -0,0 +1,601 @@
import React, {
FC,
MutableRefObject,
memo,
useEffect,
useRef,
useState,
useCallback,
Fragment,
} from "react";
import {
message,
Dropdown,
Icon,
Menu,
Button,
Tooltip,
Avatar,
Modal,
Switch,
Input,
Popconfirm,
Checkbox,
} from "antd";
import { Link } from "react-router-dom";
import { usePersistFn } from "ahooks";
import { getImageUrl, timeAgo } from "educoder";
import "./style.module.less";
import { useAcr, useNote, useGlobal, usePr } from "../../model";
const AnnotationStatus = {
Init: 'Init',
Ignore: 'Ignore',
Confirm: 'Confirm',
FalsePositive: 'FalsePositive',
}
const noteType = {
comment: 'comment',
problem: 'problem',
}
export const Commenting = (props) => {
const { user } = useGlobal();
const { addComment } = useNote();
const { toVersion, stDiff } = useAcr();
const submit = usePersistFn(async (note, type) => {
try {
console.log(note, type);
await addComment({
note,
diffId: toVersion.id,
line_code: props.lineCode,
parent_id: props.id,
path: props.path,
type: type || 'comment',
diff: props.id ? null : stDiff,
});
props.onClose && props.onClose();
} catch (e) {
console.error(e);
}
});
const { replyNote } = props;
const [note, setNote] = useState("");
const [isProblem, setIsProblem] = useState(false);
const onSubmit = useCallback(() => {
const submitNote = replyNote ? insertReplyNote(note, replyNote) : note;
submit(submitNote, isProblem ? noteType.problem : noteType.comment);
}, [note, replyNote, isProblem]);
function insertReplyNote(baseNote, replyNote) {
if (replyNote && replyNote.note) {
const replyLine = replyNote.note.split("\n");
const blockquoteNote = [
`<!-- reply id="${replyNote.id}" -->`,
...replyLine,
]
.map((note) => `> ${note}`)
.join("\n");
return blockquoteNote + "\n\n" + baseNote;
} else {
return baseNote;
}
}
return (
// <div className="arc-container">
<div
className={`arc-commenting-container ${props.noPadding ? "no-padding" : ""}`}>
<div className="avatar">
<Tooltip title={user.name}>
<Avatar src={user && user.image_url
? getImageUrl(`/${user.image_url}`)
: "images/avatars/User/b"} size={24} />
</Tooltip>
</div>
<Input.TextArea
autoFocus
placeholder="请输入评论信息"
value={note}
onChange={(e) => setNote(e.target.value)}
style={{ height: 150 }}
/>
<div className="action">
<div>
<Button
type="primary"
onClick={onSubmit}
disabled={!note.trim()}
style={{ marginRight: 8 }}
>
评论
</Button>
{note.trim() ? (
<Popconfirm
title="你确定要取消?"
okText="确定"
cancelText="取消"
onConfirm={props.onClose}
>
<Button>取消</Button>
</Popconfirm>
) : (
<Button onClick={props.onClose}>取消</Button>
)}
</div>
<div>
{replyNote && (
<div className="reply-note">Reply to {replyNote.author.name}</div>
)}
</div>
</div>
</div>
// </div>
);
};
const ReplyItem = memo(({ note, isChild }) => {
const { user } = useGlobal();
const { deleteComment } = useNote();
const [visible, setVisible] = useState(false);
return <div className={`reply-item ${isChild ? 'reply-item-child' : ''}`}>
<div className="reply-item-head">
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link"
>
<Avatar src={note.user && note.user.imageUrl
? getImageUrl(`/${note.user.imageUrl}`)
: "images/avatars/User/b"} size={24} />
</Link>
<Link
to={`/${note.user && note.user.login}`}
className="show-user-link color-black ml10 fwb"
>
{note.user && note.user.username}
</Link> <span className="show-user-link color-black">{timeAgo(note.createdAt)}</span> 发表评论
<span className="btn-right">
{user &&
(user.admin ||
user.login === note.user.login) ? (
<Popconfirm
placement="bottom"
title={"确定要删除当前评论吗?"}
okText="是"
cancelText="否"
onConfirm={() => deleteComment(note.id)}
>
<Button type="link">
<i className="iconfont icon-shanchu3 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">删除</span>
</Button>
</Popconfirm>
) : (
""
)}
{!isChild ? <Button
type="link"
className="ml-10"
onClick={() => setVisible(true)}
>
<i className="iconfont icon-huifu1 font-15 color-grey-6 mr5 ver-middle"></i>
<span className="font-12 color-grey-6">回复</span>
</Button> : ''}
</span>
</div>
<div className="reply-item-content">{note.note}</div>
{visible && <Commenting style={{ paddingLeft: '20px' }} onClose={() => { setVisible(false) }} {...note} />}
</div>
})
export const DiscussionItem = memo((props) => {
const { noteId } = props;
const { commentPack, deleteComment } = useNote();
// const [commenting, setCommenting] = useState(false);
// const [commentReply, setCommentReply] = useState();
const replyIdSet = commentPack.noteIdToReplyIdSet.get(noteId);
const replyIds = replyIdSet
? Array.from(replyIdSet).sort((a, b) => a - b)
: null;
// const pending = (function () {
// if (!commentPack.hasPendingReview) {
// return false;
// }
// const reviewId = commentPack.noteIdToReviewId.get(noteId);
// if (!reviewId) return false;
// const review = reviewId ? commentPack.reviewIdToReview.get(reviewId) : null;
// if (!review) return false;
// return review.pending;
// })();
// const hasPendingNote = (function () {
// if (pending) return true;
// if (replyIds) {
// for (const replyId of replyIds) {
// const reviewId = commentPack.noteIdToReviewId.get(replyId);
// if (reviewId && commentPack.reviewIdToReview.get(reviewId) && commentPack.reviewIdToReview.get(reviewId).pending) {
// return true;
// }
// }
// }
// return false;
// })();
// const handleReply = (replyNote) => {
// if (replyNote) {
// setCommentReply(replyNote);
// }
// setCommenting(true);
// };
// useEffect(() => {
// if (!commenting) {
// setCommentReply(undefined);
// }
// }, [commenting]);
const note = commentPack.noteIdToNote.get(noteId);
if (!note) return null;
// console.log('note');
// console.log(note);
return (
<div className="arc-container">
{/* <div>
{note.type === noteType.problem && (
<Fragment>
{note.state === "resolved" && !pending && "已解决"}
{note.state === "opened" && !pending && "待解决"}
{pending && "需要回应"}
</Fragment>
)}
</div> */}
<ReplyItem note={note} />
{replyIds && (
<div style={{ paddingLeft: 50 }}>
{replyIds.reverse().map((replyId) => {
const note = commentPack.noteIdToNote.get(replyId);
if (!note) return null;
return <ReplyItem note={note} key={replyId} isChild={true} />
})}
</div>
)}
</div>
);
});
export const Menubar = memo((props) => {
const [isFullscreen, setFullscreen] = React.useState(
props.initialFullscreen || false
);
const handleChangeFullscreen = (isFullscreen) => {
setFullscreen(isFullscreen);
if (isFullscreen) {
message.info("全屏模式支持直接使用 ↑/↓ 切换变更文件");
}
props.handleFullscreenChange(isFullscreen);
props.logFullScreen(isFullscreen);
};
useEffect(() => {
if (isFullscreen) {
document.body.style.overflow = "hidden";
}
return () => {
document.body.style.overflow = "";
};
}, [isFullscreen]);
const { versions, fromVersion, toVersion, updateQuery } = useAcr();
const idToVerbose = new Map();
idToVerbose.set(0, "Base Version");
versions.forEach((version, index) => {
if (index === versions.length - 1) {
idToVerbose.set(version.id, `Latest Version`);
} else {
idToVerbose.set(version.id, `Version ${versions.length - index - 1}`);
}
});
const fromId = fromVersion && fromVersion.id || 0;
const toId = toVersion && toVersion.id || 0;
const filterCleared = fromId === 0 && toId === versions[0] && versions[0].id;
function renderMenuItem(version, selectedId) {
const verbose = idToVerbose.get(version.id);
return (
<Menu.Item
className={`arc-menuItem ${version.id === selectedId ? "selected" : ""
}`}
key={version.id}
>
<div className='headRow'>
<span>{verbose}</span>
<span>{version.headCommitSha.slice(0, 8)}</span>
</div>
<div className='desc'>
包含 {version.commitsCount}次提交{version.filesCount} 份文件变更
</div>
</Menu.Item>
);
}
const menuFrom = (
<Menu
onClick={(e) => {
const val = parseInt(e.key);
updateQuery({
from: val === 0 ? undefined : val,
});
}}
>
{versions.slice(1).map((version) => renderMenuItem(version, fromId))}
<Menu.Item
key={0}
className={`${'menuItem'} ${fromId === 0 ? 'selected' : ""}`}
>
Base Version
</Menu.Item>
</Menu>
);
const menuTo = (
<Menu
onClick={(e) => {
updateQuery({
to: parseInt(e.key),
});
}}
>
{versions
.slice(0, versions.length)
.map((version) => renderMenuItem(version, toId))}
</Menu>
);
const MenuStyle = {
zIndex: 1200,
};
return (
<div className="arc-menubar">
<div className={'group'}>
{versions.length !== 0 && (
<div className={'versionContainer'}>
<Dropdown overlay={menuFrom} trigger={["click"]} overlayStyle={MenuStyle}>
<div className={'versionItem'}>
<span>{idToVerbose.get(fromId)}</span>
<Icon type="down" />
</div>
</Dropdown>
<div>
<Icon type="arrow-right" />
</div>
<Dropdown overlay={menuTo} trigger={["click"]} overlayStyle={MenuStyle}>
<div className={'versionItem'}>
<span>{idToVerbose.get(toId)}</span>
<Icon type="down" />
</div>
</Dropdown>
{!filterCleared && (
<div>
<a
onClick={() => {
updateQuery({
from: undefined,
to: undefined,
});
}}
>
重置
</a>
</div>
)}
</div>
)}
</div>
<div className={'group'}>
{/* {isFullscreen ? null : (
<div className={'switchContainer'}>
<span className={'ideLabel'}>IDE 模式</span>
<Tooltip
placement="top"
title="提供 IDE 代码浏览体验、完善的语言服务和高效的代码编辑"
>
<Switch checked onChange={props.toggleViewerType} />
</Tooltip>
</div>
)} */}
{isFullscreen ? (
<Icon
type="fullscreen-exit"
onClick={() => handleChangeFullscreen(false)}
/>
) : (
<Tooltip title="全屏">
<Icon
type="fullscreen"
onClick={() => handleChangeFullscreen(true)}
/>
</Tooltip>
)}
</div>
</div>
);
});
export const AnnotationEntry = memo((props) => {
const { annotation, checkSuite } = props;
//
const anchorOffsetRef = useRef(null);
const hash = `#annotation_${annotation.id}`;
const hashActive = hash === window.location.hash;
useEffect(() => {
setTimeout(() => {
if (hashActive) {
anchorOffsetRef.current && anchorOffsetRef.current.scrollIntoView(true);
}
}, 500);
});
const [isIgnore, setIsIgnore] = useState(
annotation.feedBackStatus === AnnotationStatus.Ignore
);
return (
<div className="arc-container">
<div
ref={anchorOffsetRef}
id={`annotation_${annotation.id}`}
className="arc-annotationContainer"
>
<div className={'codeLine'}>
<div>{annotation.level}</div>
<div className={'line'}>
{annotation.startLine === annotation.endLine ? (
<span>
{annotation.endLine}
行代码分析
</span>
) : (
<span>
{annotation.startLine}{annotation.endLine}
行代码分析
</span>
)}
</div>
</div>
<div className={'botRow'}>
<Avatar
alt={checkSuite.service.nameShow}
size={20}
src="https://gw-office.alipayobjects.com/bmw-prod/a9596840-928c-4603-a865-b24373ab4b4d.png"
/>
<div>
<div className={'strong'}>{checkSuite.service.nameShow}</div>
</div>
</div>
<div className={'content'}>
<p>{annotation.title}</p>
<div>{annotation.message}</div>
</div>
<div className={'detail'}>
<div>
{annotation.bugId ? (
isIgnore ? (
<Button type="primary" style={{ marginRight: 8 }}>
取消忽略
</Button>
) : (
<Fragment>
<Button style={{ marginRight: 8 }}>忽略</Button>
<Button style={{ marginRight: 8 }}>误报</Button>
<Button>确认</Button>
</Fragment>
)
) : null}
</div>
<div>
<a
className={'halfPlainA'}
href={`https://codeinsightapi.alipay.com/api/v1/describe?bug_type=${annotation.bugType}&bug_id=${annotation.bugId}`}
target="_blank"
>
查看问题详情 <Icon type="double-right" />
</a>
</div>
</div>
</div>
</div>
);
});
export const PRMoreActionLinks = ({
setVisible,
}) => {
const { pr } = usePr();
const { project } = useGlobal();
const [checkoutBranchVisible, setCheckoutBranchVisible] = useState(false);
return (
<Fragment>
<div>正在丰富功能中...</div>
<div style={{display:'none'}}>
<Switch checked />
</div>
{/* <div
className="arc-checkoutBranch"
onClick={() => {
setCheckoutBranchVisible(true);
setVisible(false);
}}
>
检出分支
</div>
<a
className="arc-flexA"
download
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.patch`}
>
<span onClick={() => setVisible(false)}>下载 patch 文件</span>
</a>
<a
className="arc-flexA"
download
href={`http://code.test.alipay.net/${project.pathWithNamespace}/pull_requests/${pr.iid}.diff`}
>
<span onClick={() => setVisible(false)}>下载 diff 文件</span>
</a>
<Modal
title="检出,评审,并在本地执行合并"
visible={checkoutBranchVisible}
footer={null}
onCancel={() => setCheckoutBranchVisible(false)}
>
<div className="arc-checkout">
<p>
<span className="fw">Step 1.</span> Fetch and check out the branch
for this pull request
</p>
<pre>
git fetch origin
<br />
{`git checkout -b ${pr.sourceBranch} origin/${pr.sourceBranch}`}
</pre>
<p>
<span className="fw">Step 2.</span> Review the changes locally
</p>
<p>
<span className="fw">Step 3.</span> Merge the branch and fix any
conflicts that come up
</p>
<pre>
git fetch origin
<br />
{`git checkout origin/${pr.targetBranch}`}
<br />
git merge --no-ff master
</pre>
<p>
<span className="fw">Step 4.</span> Push the result of the merge
</p>
<pre>{`git push origin ${pr.targetBranch}`}</pre>
</div>
</Modal> */}
</Fragment>
);
};

View File

@ -0,0 +1,219 @@
.arc-container {
padding:5px 0 10px;
border-bottom: solid 1px #eee;
.reply-item{
padding:6px 16px 10px;
&.reply-item-child{
margin-top:-10px;
padding:5px 0;
}
}
.reply-item-head{
line-height: 30px;
color: #666;
}
.btn-right{
float: right;
}
.reply-item-content{
padding-left:35px;
line-height: 1.4;
}
.show-user-link{
font-weight: 600;
}
}
.arc-menubar {
display: flex;
align-items: center;
justify-content: space-between;
.group {
display: flex;
align-items: center;
> * {
margin-right: 12px;
transition: 300ms all ease-in-out;
&:last-child {
margin-right: 0;
}
}
}
.versionContainer {
display: flex;
align-items: center;
> *:not(:last-child) {
margin-right: 2px;
}
}
.versionItem {
cursor: pointer;
padding: 4px 6px;
.anticon {
margin-left: 4px;
}
}
.switchContainer {
display: flex;
align-items: center;
}
.ideLabel {
line-height: 1;
margin-right: 8px;
}
}
.arc-menuItem {
background: #ffffff;
width: 350px;
padding-top: 8px;
padding-bottom: 8px;
&.selected {
background: #f0f5ff;
}
.headRow {
display: flex;
justify-content: space-between;
}
.desc {
margin-top: 2px;
color: rgba(0, 0, 0, 0.45);
}
}
.arc-annotationContainer {
border-bottom: solid 1px #eee;
&:last-child {
border-bottom: none;
}
.codeLine {
display: flex;
align-items: center;
}
.line {
padding-top: 8px;
padding-left: 8px;
font-size: 12px;
&.active {
color: rgba(0, 0, 0, 0.45);
}
}
.botRow {
padding: 8px 12px 0 16px;
display: flex;
align-items: center;
> *:not(:first-child) {
margin-left: 8px;
}
}
.content {
padding: 8px 12px 12px 48px;
p {
margin-bottom: 8px;
}
}
.strong {
color: rgba(0, 0, 0, 0.85);
}
.detail {
height: 50px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
padding: 0 21px;
border-top: 1px solid #ebedf0;
}
.halfPlainA {
color: inherit;
&:hover {
color: #2f54eb;
}
}
}
.arc-checkoutBranch {
font-size: 14px;
color: #2f54eb;
cursor: pointer;
line-height: 32px;
}
.arc-flexA {
display: flex !important;
justify-content: space-between;
align-items: center;
line-height: 32px;
color: #2f54eb;
i {
color: #2f54eb;
}
&:hover {
color: #2f54eb;
}
}
.arc-checkout {
pre {
margin-bottom: 8px;
padding: 8px 12px;
background-color: #fbfbfb;
border: 1px solid #ebedf0;
border-radius: 4px;
}
}
.arc-commenting-container {
position: relative;
padding: 8px 12px 0 44px;
&.no-padding {
padding: 0 0 0 32px;
.avatar {
top: 2px;
left: 0;
}
}
.avatar {
position: absolute;
top: 8px;
left: 12px;
}
.action {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
.reply-note {
padding: 1px 5px;
color: #8c8c8c;
line-height: 20px;
background-color: #fafafa;
}
}
}

View File

@ -0,0 +1,68 @@
import { apiService } from './api.service';
// 语言服务接口
export const lsifService = {
async lsifExists(projectPath, sha) {
return !!(await apiService.post(
`/webapi/projects/${encodeURIComponent(
projectPath
)}/repository/lsif/exists`,
{
sha,
}
)) ;
},
async lsifHover(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/hover`,
undefined,
{
...data,
method: 'hover',
}
))
},
async lsifDefinitions(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/definitions`,
undefined,
data
))
},
async lsifReferences(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/references`,
undefined,
data,
{
disableBodyConvert: true,
}
))
},
async lsifReferencesV2(
projectId,
data
) {
return (await apiService.post(
`/webapi/projects/42422/repository/lsif/reference/v2`,
undefined,
data,
{
disableBodyConvert: true,
}
))
},
};

View File

@ -0,0 +1,197 @@
import { apiService } from './api.service';
import { message } from 'antd';
import { getImageUrlAbsolute } from "educoder";
import { calcChangeLineNum } from './utils/calc-change-line-num';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
import sha1 from 'sha1';
export const prService = {
async getPRByIid(projectsId, owner, mergeId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}.json`);
let data = underscoreToCamelcase(res.data);
let newData = {
...data,
checkSuites: [],
iid: data.index,
sourceBranch: data.head,
sourceProjectId: projectsId,
// state: "opened",
// targetBranch: "master",
// targetProjectId: 42422,
diff: {
baseCommitSha: data.baseCommitSha,
commitsCount: data.commitNum,
headCommitSha: data.headCommitSha,
startCommitSha: data.mergeBase,
// delLineNum: 379,
// addLineNum: 753,
// createdAt: "2021-05-20T14:27:46+0800",
// filesCount: 22,
// overflow: false,
// startCommitSha: "2566c6dec7756e51f7f16267d9a2a63116ac015b",
// updatedAt: "2021-05-20T14:27:46+0800",
},
};
return newData;
},
async getDiffVersions(projectsId, owner, mergeId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions.json`);
return Array.isArray(res.data.versions) ? underscoreToCamelcase(res.data.versions) : [];
},
async getDiffs(
projectId,
owner,
from,
to,
) {
const res = await axios.get(`/v1/${owner}/42422/compare.json`, { params: { from, to } })
const changeLineNum = calcChangeLineNum(res.diffs);
return {
...res,
...changeLineNum,
};
},
async getDiffOverviews(projectsId, owner, mergeId, versionId) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`);
let files = res.data.files;
for (const [i, item] of files.entries()) {
// item.aMode= "100644";
// item.bMode= "0";
// item.markAsRead= true;
// item.tooLarge= false;
// item.updatedAfterRead= false;
item.compareDiffId = versionId;
item.id = item.name;
item.addLineNum = item.addition;
item.binaryFile = item.is_bin;
item.delLineNum = item.deletion;
item.deletedFile = item.is_deleted;
item.newFile = item.is_created;
item.newPath = item.name;
item.oldPath = item.oldname;
item.renamedFile = item.is_renamed;
}
return Array.isArray(res.data.files) ? underscoreToCamelcase(res.data.files) : []
},
async getCommentPack(projectsId, owner, mergeId,params) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals.json`,{params});
let journals=underscoreToCamelcase(res.data.journals);
for(const item of journals){
item.author=item.user;
item.author.avatarUrl=item.user.imageUrl?getImageUrlAbsolute(item.user.imageUrl):'';
item.author.username=item.user.name;
item.type='Common';
item.stDiff=item.diff;
item.discussionId=item.parentId
// item.lineType='old';
}
return journals;
},
async editPRComment(projectsId, owner, mergeId, noteId, data) {
const res = await axios.patch(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals/${noteId}.json`, data);
return res.data;
},
async getDiffById(
projectsId, owner, mergeId, versionId,
params = {}
) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/versions/${versionId}/diff.json`, { params });
let data = res.data;
data.compareDiffId = versionId;
data.id = data.name;
data.addLineNum = data.addition;
data.binaryFile = data.is_bin;
data.delLineNum = data.deletion;
data.deletedFile = data.is_deleted;
data.newFile = data.is_created;
data.newPath = data.name;
data.oldPath = data.oldname;
data.renamedFile = data.is_renamed;
return data;
},
async getFileReadMarks(projectsId, owner, mergeId,) {
const res = await axios.get(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_files.json`);
let data=[];
for (const item of res.data.files) {
if(item.mark_as_read){
item.newPath = item.name;
item.file_path_sha2 = item.file_path_sha;
item.file_path_sha = sha1(item.newPath);
data.push(item);
}
}
return underscoreToCamelcase(data);
},
async markFileAsRead(projectsId, owner, mergeId, data) {
const res = await axios.put(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_file_as_read.json`, data);
return res.data;
},
async markFileAsUnread(projectsId, owner, mergeId, data) {
const res = await axios.put(`/${owner}/${projectsId}/pulls/${mergeId}/diffs/mark_file_as_unread.json`, data);
return res.data;
},
async addComment(projectsId, owner, mergeId, data) {
const res = await axios.post(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals.json`, data);
let commentData=underscoreToCamelcase(res.data);
commentData.author=commentData.user;
commentData.author.avatarUrl=commentData.user.imageUrl?getImageUrlAbsolute(commentData.user.imageUrl):'';
commentData.author.username=commentData.user.name;
commentData.type='Common';
commentData.stDiff=commentData.diff;
commentData.discussionId=commentData.parentId
return commentData;
},
async deleteComment(projectsId, owner, mergeId,id){
const res = await axios.delete(`/v1/${owner}/${projectsId}/pulls/${mergeId}/journals/${id}.json`);
return res.data;
},
async createReview(projectsId, owner, mergeId, data) {
const res = await axios.post(`/v1/${owner}/${projectsId}/pulls/${mergeId}/reviews.json`, data);
console.log('createReview--data');
if (res.data.id) {
return res.data;
} else {
message.error(res.data && res.data.message);
}
},
async getReviews(projectsId, owner, mergeId,) {
const res = await axios.get(`/v1/${owner}/${projectsId}/pulls/${mergeId}/reviews.json`,);
let reviews=underscoreToCamelcase(res.data.reviews);
for(const item of reviews){
item.author=item.reviewer;
item.author.avatarUrl=getImageUrlAbsolute(item.reviewer.imageUrl);
item.author.username=item.reviewer.name;
item.author.webUrl='';
item.body=item.content;
item.pending=false;
item.pullRequestId=mergeId;
}
return reviews;
},
async commitReview(projectsId, owner, mergeId, body) {
return await apiService.put(
`/api/v3/projects/42422/pull_requests/13055/reviews`,
undefined,
{
body,
}
);
},
};

View File

@ -0,0 +1,41 @@
import { apiService } from './api.service';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
export const projectService = {
async getUser() {
return apiService.get(`/api/v3/user`);
},
async getProject(namespace, projectName) {
return (await apiService.get(
`/webapi/projects/${namespace}/${projectName}/`
))
},
async getFileBlob(projectsId, owner,options={}) {
const res = await axios.get(`/${owner}/${projectsId}/sub_entries.json`,{params:options});
return res.data.entries.content;
},
async getLanguages(
projectId,
params
) {
return (await apiService.get(
`/api/v4/projects/42422/languages`,
params,
{
disableResponseConvert: true,
}
))
},
async bulkChangeFiles(
projectsId, owner,data
) {
const res = await axios.post(`/v1/${owner}/${projectsId}/contents/batch`,data);
return res;
},
};

View File

@ -0,0 +1,78 @@
import { apiService } from './api.service';
import { underscoreToCamelcase } from './utils/camelcase-convert';
import axios from 'axios';
import moment from 'moment';
const format = "YYYY-MM-DD HH:mm:ss";
export const repoService = {
async getFileList(
projectId,
refName,
path,
withCommit
) {
return (await apiService.get(
`/webapi/projects/42422/repository/tree`,
{
refName,
path,
withCommit,
}
))
},
async getTreeEntry(projectId, refName, path) {
return (await apiService.get(
`/api/v3/projects/42422/repository/tree_entry`,
{
refName,
path,
}
))
},
async getCodeSymbols(projectId, ref, filepath) {
return await apiService.get(
`/api/v3/projects/42422/repository/file_symbols/${encodeURIComponent(
ref
)}/`,
{
filepath,
}
);
},
async getCodeBlame(projectsId, owner, params) {
const res = await axios.get(`/v1/${owner}/${projectsId}/blame.json`, { params });
let blame_parts = underscoreToCamelcase(res.data.blame_parts);
let blameParts = [];
for (const item of blame_parts) {
let blamePart = {
commit: {
author: item.commit.author,
// authorEmail: "taian.lta@test.com",
authorName: item.commit.author.name,
authoredDate: moment(new Date(item.commit.authoredTime * 1000)).format(format),
committedDate: moment(new Date(item.commit.committedTime * 1000)).format(format),
committer: item.commit.committer,
// committerEmail: "taian.lta@test.com",
committerName: item.commit.committer.name,
createdAt: moment(new Date(item.commit.createdTime * 1000)).format(format),
id: item.commit.sha,
message: item.commit.commitMessage,
// parentIds: null,
// shortId: "7c501a19",
title: item.commit.commitMessage,
// treeHash: null,
},
lines: [{
currentNumber: item.currentNumber,
effectLine: item.effectLine,
}]
};
blameParts.push(blamePart);
}
return blameParts;
},
};

View File

@ -0,0 +1,20 @@
import { mockService } from './mock';
import { underscoreToCamelcase } from '../utils/camelcase-convert';
export async function request(
url,
options,
extraOptions
) {
const data = underscoreToCamelcase(mockService[url]);
console.log('request ==== ', url);
console.log(data)
if (data) {
return data;
} else {
return '文件展示内容只mock了 aaa/package.json';
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,32 @@
import { message as messageTip } from 'antd';
export function createApiError(message = '请求出错', response) {
const err = new Error(message);
err.isApiError = true;
err.response = response;
return err;
}
export function isApiError(e) {
return e&&e.isApiError;
}
/**
* 目前存在一些请求成功也报错的情况
* 返回值可能情况
* 1. success === true 此时只提示有 errorMessage 的情况
* 2. success === false 此时提示包括 message 的信息
* todo: 目前由于intl使用的是react component注入的方式这里暂时无法做到国际化后续等移除 konjac 之后采用全局api方式即可支持国际化
* @param responseData 返回值各种可能
*/
export function successErrorMessage(responseData) {
if (responseData) {
const errorMessage = responseData.error || responseData.errorMessage;
const message = errorMessage || responseData.message;
if (responseData.success === false && message) {
messageTip.error(message);
} else if (errorMessage) {
messageTip.error(errorMessage);
}
}
}

View File

@ -0,0 +1,20 @@
export function calcChangeLineNum(diffs) {
let addLineNum = 0;
let delLineNum = 0;
for (const diff of diffs) {
if (diff.tooLarge) {
continue;
}
const addition = diff.diff.match(/^\+/gm)&&diff.diff.match(/^\+/gm).length || 0;
const deletion = diff.diff.match(/^-/gm)&&diff.diff.match(/^-/gm).length || 0;
diff.addLineNum = addition;
diff.delLineNum = deletion;
addLineNum += addition;
delLineNum += deletion;
}
return {
addLineNum,
delLineNum,
};
}

View File

@ -0,0 +1,35 @@
export function underscoreToCamelcase(obj){
if (Array.isArray(obj)) {
return obj.map((item) => underscoreToCamelcase(item))
}
if (typeof obj !== 'object' || obj === null || obj === undefined) {
return obj;
}
const ret = {};
for (const key in obj) {
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
continue;
}
const newKey = key.replace(/_([a-z])/g, (m) => m[1].toUpperCase());
ret[newKey] = underscoreToCamelcase(obj[key]);
}
return ret
}
export function camelcaseToUnderscore(obj) {
if (Array.isArray(obj)) {
return obj.map((item) => camelcaseToUnderscore(item))
}
if (typeof obj !== 'object' || obj === null || obj === undefined) {
return obj;
}
const ret= {};
for (const key in obj) {
if (obj.hasOwnProperty && !obj.hasOwnProperty(key)) {
continue;
}
const newKey = key.replace(/([a-z][A-Z])/g, (m) => m[0] + '_' + m[1].toLowerCase());
ret[newKey] = camelcaseToUnderscore(obj[key]);
}
return ret
}

View File

@ -0,0 +1,2 @@
export const getLocale = () =>
document.cookie.indexOf('LOCALE=en_US') > -1 ? 'en-US' : 'zh-CN';

View File

@ -0,0 +1,8 @@
export function mapGetSet(map, key, fallback) {
let ret = map.get(key);
if (!ret) {
ret = fallback();
map.set(key, ret);
}
return ret;
}

View File

@ -0,0 +1,682 @@
import React, {
useState,
useMemo,
useRef,
useContext,
useEffect,
useCallback,
createContext,
createElement,
FC,
} from 'react';
import { getLocale } from './mock/utils/locale';
// @ts-ignore
import sha1 from 'sha1';
// @ts-ignore
import memoize from 'lodash/memoize';
import jCookie from 'js-cookie';
import { useRequest } from './hooks';
// import { group, repo, prIid, project } from './meta';
import { prService } from './mock/pr.service.js';
import { projectService } from './mock/project.service.js';
import { mapGetSet } from './mock/utils/map-get-set.js';
import { isApiError } from './mock/utils/api-error.js';
import { mockService } from './mock/request/mock';
import { Base64 } from 'js-base64';
import './style.less';
const NoteType = {
comment: 'Comment',
problem: 'Problem',
}
const NoteState = {
opened: 'opened',
resolved: 'resolved',
invalid: 'invalid',
}
const CharsetName = {
gbk: 'GBK',
utf8: 'UTF-8',
};
function createContainer(useHook, checkNull = false) {
let Context = createContext(null);
const Provider = ({ children }) => {
const value = useHook();
if (checkNull && !value) return null;
return <Context.Provider value={value}>{children}</Context.Provider>;
};
const useContainer = () => React.useContext(Context);
return [Provider, useContainer];
}
let ContextProps = createContext(null);
const useGlobal = () => React.useContext(ContextProps);
const [PrProvider, usePr] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const data = useRequest(async () => {
const pr = await prService.getPRByIid(projectsId, owner, mergeId);
return { pr }
})
return data;
}, true);
const [SettingProvider, useSetting] = createContainer(() => {
const { project } = useGlobal();
const [gbk, setGBK] = useState(() => project.encoding === 'GBK');
const charsetName = gbk ? CharsetName.gbk : CharsetName.utf8;
const [locale, setLocal] = useState(() => getLocale());
const setLocale = React.useCallback(() => {
jCookie.set('LOCALE', locale === 'en-US' ? 'zh_CN' : 'en_US');
window.location.reload();
}, [locale]);
return {
gbk,
setGBK,
charsetName,
locale,
setLocale,
};
});
//
/* note review /webapi/projects/42422/pull_requests/13055/comments mock
* note格式 参照 types/note.ts
* review 参照 types/review.ts
* review 包含 reviewNotes 内有多个note
*
* 需传递的数据
* noteIdToReplyIdSet noteIdToNote lineToNoteIdSet updateFlag
*/
const useCommentPack = (projectId, prId) => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
// ACR
const lineToNoteIdSet = useMemo(() => new Map(), []);
// note noteid => note
const noteIdToNote = useMemo(() => new Map(), []);
const noteIdToReviewId = useMemo(() => new Map(), []);
// review reviewid => review
const reviewIdToReview = useMemo(() => new Map(), []);
// 使
const noteIdToReplyIdSet = useMemo(() => new Map(), []);
// review
const pendingNoteIdSet = useMemo(() => new Set(), []);
const recordNoteIdSet = useMemo(() => new Set(), []);
const topLevelCommentNoteIdSet = useMemo(() => new Set(), []);
//
const commentNoteIdSet = useMemo(() => new Set(), []);
const openedProblemNoteIdSet = useMemo(() => new Set(), []);
const [updateFlag, setUpdateFlag] = useState({});
const fetchingRef = useRef(false);
const lastFetchAtRef = useRef();
const pendingReviewRef = useRef();
const [hasPendingReview, setHasPendingReview] = useState(false); // TODO: state
function processNote(note, review = null) {
noteIdToNote.set(note.id, note);
noteIdToReviewId.set(note.id, review && review.id);
if (note.type === NoteType.problem && note.state === NoteState.opened) {
openedProblemNoteIdSet.add(note.id);
} else {
openedProblemNoteIdSet.delete(note.id);
}
if (!note.discussionId) {
if (!note.lineCode && note.system) {
recordNoteIdSet.add(note.id);
} else {
topLevelCommentNoteIdSet.add(note.id);
}
}
if (note.discussionId || note.lineCode) {
commentNoteIdSet.add(note.id);
}
if (note.lineCode) {
const noteIdSet = mapGetSet(
lineToNoteIdSet,
note.lineCode,
() => new Set()
);
noteIdSet.add(note.id);
}
if (note.discussionId) {
const replyIdSet = mapGetSet(
noteIdToReplyIdSet,
note.discussionId,
() => new Set()
);
replyIdSet.add(note.id);
}
if (review && review.pending) {
pendingNoteIdSet.add(note.id);
} else {
pendingNoteIdSet.delete(note.id);
}
}
//
async function doRefresh(force = false, filePath) {
if (fetchingRef.current) {
return;
}
fetchingRef.current = true;
//
const commentPack = await prService.getCommentPack(projectsId, owner, mergeId, { path: filePath, is_full: true });
const reviews = await prService.getReviews(projectsId, owner, mergeId);
// debugger
for (const review of reviews) {
review.reviewNotes = commentPack;
}
const pack = {
committedReviews: reviews,
notes: commentPack,
pendingReview: reviews[0],
};
console.log('pack');
console.log(pack);
// debugger
lastFetchAtRef.current = pack.currentFetchedAt;
let updated = false;
if (force) {
lineToNoteIdSet.clear();
noteIdToNote.clear();
noteIdToReviewId.clear();
reviewIdToReview.clear();
noteIdToReplyIdSet.clear();
pendingNoteIdSet.clear();
recordNoteIdSet.clear();
topLevelCommentNoteIdSet.clear();
commentNoteIdSet.clear();
openedProblemNoteIdSet.clear();
updated = true;
}
for (const note of pack.notes) {
processNote(note);
updated = true;
}
for (const review of pack.committedReviews) {
reviewIdToReview.set(review.id, review);
for (const note of review.reviewNotes) {
processNote(note, review);
}
updated = true;
}
if (pack.pendingReview) {
reviewIdToReview.set(pack.pendingReview.id, pack.pendingReview);
pendingReviewRef.current = pack.pendingReview;
setHasPendingReview(true);
for (const note of pack.pendingReview.reviewNotes) {
processNote(note, pack.pendingReview);
updated = true;
}
} else {
setHasPendingReview(false);
}
if (updated) {
setUpdateFlag({});
}
fetchingRef.current = false;
}
function manualUpdateReview(review) {
reviewIdToReview.set(review.id, review);
if (review.pending) {
pendingReviewRef.current = review;
setHasPendingReview(!!review);
}
setUpdateFlag({});
}
function manualAddNote(note, review = null) {
processNote(note, review);
setUpdateFlag({});
}
function manualRemoveNote(id) {
noteIdToNote.delete(id);
setUpdateFlag({});
}
function manualUpdateNote(note) {
const reviewId = noteIdToReviewId.get(note.id);
const review = reviewId ? reviewIdToReview.get(reviewId) : null;
processNote(note, review);
setUpdateFlag({});
}
function getRelatedFilePathByNoteId(noteId) {
const note = noteIdToNote.get(noteId);
if (!note) return;
let path = note.path;
if (note.discussionId) {
const parent = noteIdToNote.get(note.discussionId);
path = parent && parent.path;
}
return path;
}
//
async function editNote(
noteId, data
) {
const newNote = await prService.editPRComment(projectsId, owner, mergeId, noteId, data);
manualUpdateNote(newNote);
newNote.discussions && newNote.discussions.forEach((note) => manualUpdateNote(note));
}
const pathShaToNoteCount = (function () {
const ret = new Map();
function handleNote(note) {
if (!note || !note.lineCode) return;
const key = note.lineCode.split('_')[0];
ret.set(key, (ret.get(key) || 0) + 1);
}
topLevelCommentNoteIdSet.forEach((noteId) => {
const note = noteIdToNote.get(noteId);
handleNote(note);
});
return ret;
})();
return {
lineToNoteIdSet,
noteIdToNote,
noteIdToReviewId,
reviewIdToReview,
noteIdToReplyIdSet,
pendingNoteIdSet,
recordNoteIdSet,
topLevelCommentNoteIdSet,
openedProblemNoteIdSet,
hasPendingReview,
hasOpenedProblem: openedProblemNoteIdSet.size > 0,
doRefresh,
manualUpdateReview,
manualAddNote,
manualRemoveNote,
manualUpdateNote,
pendingReviewRef,
getRelatedFilePathByNoteId,
editNote,
updateFlag,
pathShaToNoteCount,
setUpdateFlag,
};
};
// note
const [NoteProvider, useNote] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const { pr: { id: prId } } = usePr();
const commentPack = useCommentPack(projectsId, mergeId);
// useEffect(() => {
// commentPack.doRefresh();
// }, []);
async function addComment(data) {
if (!commentPack.hasPendingReview) {
let reviewData = {
content: data.note,
status: 'common',
}
const review = await prService.createReview(projectsId, owner, mergeId, reviewData);
commentPack.manualUpdateReview(review);
}
const note = await prService.addComment(projectsId, owner, mergeId, data);
commentPack.manualAddNote(note, commentPack.pendingReviewRef.current);
}
async function deleteComment(id) {
const res = await prService.deleteComment(projectsId, owner, mergeId, id);
if (res.status == 0) {
commentPack.manualRemoveNote(id);
}
}
async function commitReview(body) {
if (!commentPack.hasPendingReview) {
let reviewData = {
content: body.note,
status: 'common',
}
await prService.createReview(projectsId, owner, mergeId, reviewData);
}
await prService.commitReview(projectsId, owner, mergeId, body);
await commentPack.doRefresh(true);
}
const activateRef = useRef();
return {
commentPack,
...commentPack,
addComment,
deleteComment,
commitReview,
prId,
activateRef,
};
});
const [ReadMarkProvider, useReadMark] = createContainer(() => {
const { params } = useGlobal();
const { projectsId, owner, mergeId } = params;
const { pr: { id: prId } } = usePr();
const [flag, updateFlag] = useState({});
const readMarks = useRequest(
() => prService.getFileReadMarks(projectsId, owner, mergeId,),
{
deps: [projectsId, owner, mergeId, flag],
}
);
const readMarkMap = useMemo(() => {
const map = new Map();
if (!readMarks) return map;
for (const mark of readMarks) {
mark.markAsRead && map.set(mark.filePathSha, mark);
}
return map;
}, [readMarks]);
const memoizedSha1 = useMemo(() => memoize(sha1), []);
async function markFileAsRead(filePath) {
const data = await prService.markFileAsRead(
projectsId, owner, mergeId,
{ file_path_sha: Base64.encode(filePath) }
);
updateFlag({});
return data;
}
async function markFileAsUnread(filePath) {
const data = await prService.markFileAsUnread(
projectsId, owner, mergeId,
{ file_path_sha: Base64.encode(filePath) }
);
updateFlag({});
return data;
}
const getFileReadMark = useCallback(
(filePath) => {
const filePathSha = memoizedSha1(filePath);
return readMarkMap.get(filePathSha);
},
[readMarkMap]
);
const getFileReadStatus = useCallback(
(filePath) => {
const readMark = getFileReadMark(filePath);
return readMark && readMark.markAsRead && !readMark.updatedAfterRead;
},
[getFileReadMark]
);
return {
readMarks,
readMarkMap,
getFileReadMark,
getFileReadStatus,
markFileAsRead,
markFileAsUnread,
};
});
const [AcrProvider, useAcr] = createContainer(() => {
const { params } = useGlobal();
const { pr } = usePr();
const { charsetName } = useSetting();
const { commentPack } = useNote();
const { projectsId, owner, mergeId } = params;
const [IDEMode, setIDEMode] = useState(true);
const toggleViewerType = useCallback(() => {
setIDEMode((v) => !v);
}, [setIDEMode]);
const [stDiff, setStDiff] = useState({});
const [search, setSearch] = useState(window.location.search);
const [acrflag, setAcrFlag] = useState();
const [versions, setVersions] = useState([]);
const query = useMemo(() => {
const searchParams = new URLSearchParams(search);
const ret = {};
if (searchParams.has('from')) {
ret.from = parseInt(searchParams.get('from'));
}
if (searchParams.has('to')) {
ret.to = parseInt(searchParams.get('to'));
}
return ret;
}, [search]);
function updateQuery(data) {
const searchParam = new URLSearchParams();
if (data.from) {
searchParam.set('from', String(data.from));
}
if (data.to) {
searchParam.set('to', String(data.to));
}
const search = searchParam.toString();
window.history.replaceState(
null,
'',
`${window.location.pathname}${search ? '?' : ''}${search}`
);
setSearch(search);
}
async function getDiffVersions() {
const data = await prService.getDiffVersions(projectsId, owner, mergeId);
if (acrflag && data.length == versions.length) {
setTimeout(() => {
setAcrFlag({});
}, 3000);
}
setVersions(data);
}
useEffect(() => {
getDiffVersions()
}, [acrflag])
const versionMap = useMemo(() => {
const map = new Map();
for (const version of versions) {
map.set(version.id, version);
}
return map;
}, [versions]);
const latestVersionId = versions[0] && versions[versions.length - 1].id || pr.diff.id;
const from = query.from;
const to = query.to || latestVersionId;
const fromVersion = from ? versionMap.get(from) : null;
const toVersion = versionMap.get(to);
console.log('diffpack', from, to, fromVersion, toVersion);
const ignoreWhiteSpace = false;
// @ts-ignore
const diffsPack = useRequest(
async () => {
if (!toVersion) return null;
const isPartial = !!fromVersion || toVersion.id !== latestVersionId;
const options = {
ignoreWhiteSpaceChange: ignoreWhiteSpace,
charsetName,
};
let res;
if (fromVersion) {
res = await prService.getDiffs(
projectsId, owner,
fromVersion.headCommitSha,
toVersion.headCommitSha,
options
);
} else {
if (ignoreWhiteSpace) {
res = await prService.getDiffs(
projectsId, owner,
toVersion.baseCommitSha,
toVersion.headCommitSha,
options
);
} else {
const diffs = await prService.getDiffOverviews(
projectsId, owner,
mergeId,
toVersion.id
);
res = {
diffs,
overflow: false,
addLineNum: toVersion.addLineNum,
delLineNum: toVersion.delLineNum,
};
}
}
return {
...res,
fromVersion,
toVersion,
isPartial,
};
},
{
deps: [fromVersion, toVersion, charsetName],
}
);
useEffect(() => {
commentPack.doRefresh();
}, []);
async function getDiffById(diffId) {
let thisStDiff = await prService.getDiffById(projectsId, owner, mergeId, toVersion.id,
{
filepath: diffId,
});
delete thisStDiff.sections
setStDiff(thisStDiff);
commentPack.doRefresh(false, diffId);
return thisStDiff;
}
async function getFileContent(path, sha, maxSize) {
try {
return await projectService.getFileBlob(projectsId, owner, {
filepath: path,
ref: sha,
});
} catch (e) {
if (isApiError(e) && e.response.status === 413) {
return null;
}
throw e;
}
}
const annotationPacks = [];
let countMap = new Map();
for (const checkSuite of pr.checkSuites) {
if (checkSuite.checkRuns) {
for (const checkRun of checkSuite.checkRuns) {
if (checkRun.annotations) {
for (const annotation of checkRun.annotations) {
const pack = {
annotation,
checkSuite,
};
annotationPacks.push(pack);
countMap.set(
annotation.path,
(countMap.get(annotation.path) || 0) + 1
);
}
}
}
}
}
return {
diffsPack,
versions,
fromVersion,
toVersion,
getDiffById,
stDiff,
getFileContent,
IDEMode,
toggleViewerType,
updateQuery,
setAcrFlag,
annotationPacks,
};
});
export const Provider = (props) => {
const match = props.match;
let project = {};
// let user = mockService['/api/v3/user'];
if (props.projectDetail) {
let details = props.projectDetail;
project = {
...project,
...props.projectDetail,
pathWithNamespace: details.full_name,
}
}
let user = {};
if (props.current_user) {
let current_user = props.current_user;
user = { ...current_user, avatar_url: current_user.image_url, name: current_user.username, };
}
return (
<ContextProps.Provider value={{
project, user, params: match.params
}}>
{/* <GlobalProvider> */}
<PrProvider>
<SettingProvider>
<NoteProvider>
<ReadMarkProvider>
<AcrProvider>{props.children}</AcrProvider>
</ReadMarkProvider>
</NoteProvider>
</SettingProvider>
</PrProvider>
{/* </GlobalProvider> */}
</ContextProps.Provider>
);
};
export { useGlobal, usePr, useNote, useReadMark, useAcr, useSetting };

View File

@ -0,0 +1,62 @@
export const ExtensionCommand ={
acrToggleBlame : 'code.blame.acrToggleBlame',
linkToCommit : 'code.blame.linktocommit',
onActive : 'code.blame.extension.active',
setProjectData : 'code.blame.setProjectData',
getBlameData : 'code.blame.getBlameData',
}
export default class IDEPlugin {
/**
* 插件 ID用于唯一标识插件
*/
PLUGIN_ID = 'ACR_BLAME_PLUGIN';
constructor(
onActive,
linkToCommit,
getBlame
) {
this.onActivate = onActive;
// 跳转
this.linkToCommit = linkToCommit;
// 传递blame数据
this.getBlame = getBlame;
}
/**
* 激活插件
*/
activate = (ctx) => {
const { commands, context } = ctx;
this.commands = commands;
context.subscriptions.push(
commands.registerCommand(ExtensionCommand.onActive, () => {
this.onActivate();
}),
commands.registerCommand(ExtensionCommand.linkToCommit, (params) => {
const { commitId } = params;
this.linkToCommit(commitId);
}),
commands.registerCommand(
ExtensionCommand.getBlameData,
async (sendData) => {
const { projectId, prevSha, nextSha, filePath } = sendData;
// kaitian内uri 和 antcode内不同 多了一个 /
const path = filePath.startsWith('/') ? filePath.slice(1) : filePath;
const response = await this.getBlame(projectId, nextSha, path).then(
(res) => {
return res;
}
);
return response;
}
)
);
};
/**
* 注销插件可在此时机清理副作用
*/
deactivate() {}
}

View File

@ -0,0 +1,109 @@
@c45: rgba(0, 0, 0, 0.45);
@c85: rgba(0, 0, 0, 0.85);
html,
body,
#main {
overflow: visible;
}
#main {
padding: 8px 24px 24px;
width: auto;
height: auto;
background-color: #f7f8fa;
}
.pr-head {
padding: 24px;
background-color: #fff;
margin-bottom: 24px;
}
.controller {
margin-bottom: 24px;
> * {
margin-right: 8px;
}
}
.monaco-editor-hover {
border: none !important;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
transform: translate(-20px, 0px);
}
.monaco-editor-hover-content .markdown-hover .hover-contents {
padding: 16px;
background: #fff;
h1 {
color: @c85;
font-size: 14px;
line-height: 22px;
}
p {
color: @c45;
}
}
.ide-logo {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.ide-btn {
margin: 4px;
display: inline-block;
}
.ide-btn {
display: inline-flex;
box-shadow: inset 0 0 35px 5px rgba(0, 0, 0, 0.05),
inset 0 2px 1px 1px rgba(255, 255, 255, 0.9),
inset 0 -2px 1px 0 rgba(0, 0, 0, 0.05);
border-radius: 8px;
background: #fefefe;
position: relative;
height: 40px;
width: 40px;
justify-content: center;
align-items: center;
font-size: 20px;
}
// .ide-btn:before {
// box-shadow: 0 0 17.5px 8.75px white;
// border-radius: 118.3px;
// background: white;
// position: absolute;
// margin-left: -50.4px;
// margin-top: -50.4px;
// opacity: 0.2;
// content: "";
// height: 100.8px;
// width: 100.8px;
// left: 50%;
// top: 50%;
// }
}
.ide-logo-img {
width: 150px;
}
.ide-logo-text {
color: #aaa;
margin-top: 0.5em;
font-size: 14px;
&:nth-child(2) {
margin-left: 2em;
}
&:nth-child(3) {
margin-left: 2em;
}
&:nth-child(4) {
margin-left: 4em;
}
}

View File

@ -1,8 +1,30 @@
.monaco-editor .view-overlays .current-line{
.monaco-editor .view-overlays .current-line {
border: none !important;
background-color: rgba(48,232,132,0.15);
background-color: rgba(48, 232, 132, 0.15);
}
.monaco-editor .margin-view-overlays .current-line{
background-color: rgba(48,232,132,0.15);
.monaco-editor .margin-view-overlays .current-line {
background-color: rgba(48, 232, 132, 0.15);
}
.branchTable .margin-view-overlays {
border-right: none !important;
background-color: #fcfcfc !important;
}
.editorBorderBox {
position: relative;
}
.ide-tool-bar {
position: absolute;
z-index: 10;
right: 114px;
top: -37px;
display: flex;
align-items: center;
}
.ant-switch-checked {
background-color: #466aff;
}
.read-more{
font-size: 18px;
margin:0 10px 0 15px;
color: #666;
}
.branchTable .margin-view-overlays{border-right: none !important; background-color: #fcfcfc !important;}

View File

@ -0,0 +1,724 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'css-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'css-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/css.png',
activationEvents: [
'onLanguage:css',
'onLanguage:less',
'onLanguage:scss',
'onCommand:_css.applyCodeAction',
],
kaitianContributes: {
workerMain: 'client/dist/browser/cssClientMain.js',
},
contributes: {
configuration: [
{
order: 22,
id: 'css',
title: '%css.title%',
properties: {
'css.customData': {
type: 'array',
markdownDescription: '%css.customData.desc%',
default: [],
items: {
type: 'string',
},
scope: 'resource',
},
'css.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%css.completion.triggerPropertyValueCompletion.desc%',
},
'css.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%css.completion.completePropertyWithSemicolon.desc%',
},
'css.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%css.validate.desc%',
},
'css.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.compatibleVendorPrefixes.desc%',
},
'css.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.vendorPrefix.desc%',
},
'css.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.duplicateProperties.desc%',
},
'css.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.emptyRules.desc%',
},
'css.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.importStatement.desc%',
},
'css.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.boxModel.desc%',
},
'css.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.universalSelector.desc%',
},
'css.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.zeroUnits.desc%',
},
'css.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%css.lint.fontFaceProperties.desc%',
},
'css.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%css.lint.hexColorLength.desc%',
},
'css.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%css.lint.argumentsInColorFunction.desc%',
},
'css.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.unknownProperties.desc%',
},
'css.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%css.lint.validProperties.desc%',
},
'css.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.ieHack.desc%',
},
'css.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.unknownVendorSpecificProperties.desc%',
},
'css.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%css.lint.propertyIgnoredDueToDisplay.desc%',
},
'css.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.important.desc%',
},
'css.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%css.lint.float.desc%',
},
'css.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%css.lint.idSelector.desc%',
},
'css.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%css.lint.unknownAtRules.desc%',
},
'css.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%css.trace.server.desc%',
},
},
},
{
id: 'scss',
order: 24,
title: '%scss.title%',
properties: {
'scss.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%scss.completion.triggerPropertyValueCompletion.desc%',
},
'scss.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%scss.completion.completePropertyWithSemicolon.desc%',
},
'scss.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%scss.validate.desc%',
},
'scss.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.compatibleVendorPrefixes.desc%',
},
'scss.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.vendorPrefix.desc%',
},
'scss.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.duplicateProperties.desc%',
},
'scss.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.emptyRules.desc%',
},
'scss.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.importStatement.desc%',
},
'scss.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.boxModel.desc%',
},
'scss.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.universalSelector.desc%',
},
'scss.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.zeroUnits.desc%',
},
'scss.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%scss.lint.fontFaceProperties.desc%',
},
'scss.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%scss.lint.hexColorLength.desc%',
},
'scss.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%scss.lint.argumentsInColorFunction.desc%',
},
'scss.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.unknownProperties.desc%',
},
'scss.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%scss.lint.validProperties.desc%',
},
'scss.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.ieHack.desc%',
},
'scss.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.unknownVendorSpecificProperties.desc%',
},
'scss.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%scss.lint.propertyIgnoredDueToDisplay.desc%',
},
'scss.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.important.desc%',
},
'scss.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%scss.lint.float.desc%',
},
'scss.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%scss.lint.idSelector.desc%',
},
'scss.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%scss.lint.unknownAtRules.desc%',
},
},
},
{
id: 'less',
order: 23,
type: 'object',
title: '%less.title%',
properties: {
'less.completion.triggerPropertyValueCompletion': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%less.completion.triggerPropertyValueCompletion.desc%',
},
'less.completion.completePropertyWithSemicolon': {
type: 'boolean',
scope: 'resource',
default: true,
description:
'%less.completion.completePropertyWithSemicolon.desc%',
},
'less.validate': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%less.validate.desc%',
},
'less.lint.compatibleVendorPrefixes': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.compatibleVendorPrefixes.desc%',
},
'less.lint.vendorPrefix': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.vendorPrefix.desc%',
},
'less.lint.duplicateProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.duplicateProperties.desc%',
},
'less.lint.emptyRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.emptyRules.desc%',
},
'less.lint.importStatement': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.importStatement.desc%',
},
'less.lint.boxModel': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.boxModel.desc%',
},
'less.lint.universalSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.universalSelector.desc%',
},
'less.lint.zeroUnits': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.zeroUnits.desc%',
},
'less.lint.fontFaceProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription: '%less.lint.fontFaceProperties.desc%',
},
'less.lint.hexColorLength': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%less.lint.hexColorLength.desc%',
},
'less.lint.argumentsInColorFunction': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'error',
description: '%less.lint.argumentsInColorFunction.desc%',
},
'less.lint.unknownProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.unknownProperties.desc%',
},
'less.lint.validProperties': {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
},
scope: 'resource',
default: [],
description: '%less.lint.validProperties.desc%',
},
'less.lint.ieHack': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.ieHack.desc%',
},
'less.lint.unknownVendorSpecificProperties': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.unknownVendorSpecificProperties.desc%',
},
'less.lint.propertyIgnoredDueToDisplay': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
markdownDescription:
'%less.lint.propertyIgnoredDueToDisplay.desc%',
},
'less.lint.important': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.important.desc%',
},
'less.lint.float': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
markdownDescription: '%less.lint.float.desc%',
},
'less.lint.idSelector': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'ignore',
description: '%less.lint.idSelector.desc%',
},
'less.lint.unknownAtRules': {
type: 'string',
scope: 'resource',
enum: ['ignore', 'warning', 'error'],
default: 'warning',
description: '%less.lint.unknownAtRules.desc%',
},
},
},
],
configurationDefaults: {
'[css]': {
'editor.suggest.insertMode': 'replace',
},
'[scss]': {
'editor.suggest.insertMode': 'replace',
},
'[less]': {
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.css-data.json',
url: 'https://raw.githubusercontent.com/microsoft/vscode-css-languageservice/master/docs/customData.schema.json',
},
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
workerMain: 'client/dist/browser/cssClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'CSS Language Features',
description: 'Provides rich language support for CSS, LESS and SCSS files.',
'css.title': 'CSS',
'css.customData.desc':
'A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-css-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its CSS support for the custom CSS properties, at directives, pseudo classes and pseudo elements you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.',
'css.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'css.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'css.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'css.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'css.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'css.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'css.lint.emptyRules.desc': 'Do not use empty rulesets.',
'css.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'css.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'css.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'css.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'css.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'css.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'css.lint.importStatement.desc':
'Import statements do not load in parallel.',
'css.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'css.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'css.lint.unknownAtRules.desc': 'Unknown at-rule.',
'css.lint.unknownProperties.desc': 'Unknown property.',
'css.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'css.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'css.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'css.lint.zeroUnits.desc': 'No unit for zero needed.',
'css.trace.server.desc':
'Traces the communication between VS Code and the CSS language server.',
'css.validate.title': 'Controls CSS validation and problem severities.',
'css.validate.desc': 'Enables or disables all validations.',
'less.title': 'LESS',
'less.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'less.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'less.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'less.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'less.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'less.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'less.lint.emptyRules.desc': 'Do not use empty rulesets.',
'less.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'less.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'less.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'less.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'less.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'less.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'less.lint.importStatement.desc':
'Import statements do not load in parallel.',
'less.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'less.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'less.lint.unknownAtRules.desc': 'Unknown at-rule.',
'less.lint.unknownProperties.desc': 'Unknown property.',
'less.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'less.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'less.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'less.lint.zeroUnits.desc': 'No unit for zero needed.',
'less.validate.title': 'Controls LESS validation and problem severities.',
'less.validate.desc': 'Enables or disables all validations.',
'scss.title': 'SCSS (Sass)',
'scss.completion.triggerPropertyValueCompletion.desc':
'By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.',
'scss.completion.completePropertyWithSemicolon.desc':
'Insert semicolon at end of line when completing CSS properties',
'scss.lint.argumentsInColorFunction.desc': 'Invalid number of parameters.',
'scss.lint.boxModel.desc':
'Do not use `width` or `height` when using `padding` or `border`.',
'scss.lint.compatibleVendorPrefixes.desc':
'When using a vendor-specific prefix make sure to also include all other vendor-specific properties.',
'scss.lint.duplicateProperties.desc':
'Do not use duplicate style definitions.',
'scss.lint.emptyRules.desc': 'Do not use empty rulesets.',
'scss.lint.float.desc':
'Avoid using `float`. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes.',
'scss.lint.fontFaceProperties.desc':
'`@font-face` rule must define `src` and `font-family` properties.',
'scss.lint.hexColorLength.desc':
'Hex colors must consist of three or six hex numbers.',
'scss.lint.idSelector.desc':
'Selectors should not contain IDs because these rules are too tightly coupled with the HTML.',
'scss.lint.ieHack.desc':
'IE hacks are only necessary when supporting IE7 and older.',
'scss.lint.important.desc':
'Avoid using `!important`. It is an indication that the specificity of the entire CSS has gotten out of control and needs to be refactored.',
'scss.lint.importStatement.desc':
'Import statements do not load in parallel.',
'scss.lint.propertyIgnoredDueToDisplay.desc':
'Property is ignored due to the display. E.g. with `display: inline`, the `width`, `height`, `margin-top`, `margin-bottom`, and `float` properties have no effect.',
'scss.lint.universalSelector.desc':
'The universal selector (`*`) is known to be slow.',
'scss.lint.unknownAtRules.desc': 'Unknown at-rule.',
'scss.lint.unknownProperties.desc': 'Unknown property.',
'scss.lint.validProperties.desc':
'A list of properties that are not validated against the `unknownProperties` rule.',
'scss.lint.unknownVendorSpecificProperties.desc':
'Unknown vendor specific property.',
'scss.lint.vendorPrefix.desc':
'When using a vendor-specific prefix, also include the standard property.',
'scss.lint.zeroUnits.desc': 'No unit for zero needed.',
'scss.validate.title': 'Controls SCSS validation and problem severities.',
'scss.validate.desc': 'Enables or disables all validations.',
'css.colorDecorators.enable.deprecationMessage':
'The setting `css.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'scss.colorDecorators.enable.deprecationMessage':
'The setting `scss.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'less.colorDecorators.enable.deprecationMessage':
'The setting `less.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,56 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'editor-plugin-blame',
version: '0.2.6',
},
packageJSON: {
name: 'editor-plugin-blame',
publisher: 'alex-ext-public',
version: '0.2.6',
repository: {
type: 'git',
url: 'http://code.alipay.com/yxy167584/editor-plugin-blame.git',
},
displayName: 'editor-plugin-blame',
description: ' ',
activationEvents: ['*'],
kaitianContributes: {
workerMain: './out/worker/index.js',
},
contributes: {
commands: [
{
command: 'code.blame.toggleBlame',
title: '查看blame',
},
{
command: 'code.blame.acrToggleBlame',
title: 'blame',
},
{
command: 'code.blame.linktocommit',
title: 'hover详情跳转',
},
],
views: {},
menus: {
'editor/title': [
{
command: 'code.blame.acrToggleBlame',
type: 'checkbox',
group: 'navigation',
toggledWhen: 'acr_blame_context',
when: 'resourceScheme =~ /^git$|^diff$/',
},
],
},
workerMain: './out/worker/index.js',
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: ['package.json', 'out/worker/index.js'],
mode: 'public',
};

View File

@ -0,0 +1,273 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'html-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'html-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/html.png',
activationEvents: ['onLanguage:html', 'onLanguage:handlebars'],
kaitianContributes: {
workerMain: 'client/dist/browser/htmlClientMain.js',
},
contributes: {
configuration: {
id: 'html',
order: 20,
type: 'object',
title: 'HTML',
properties: {
'html.customData': {
type: 'array',
markdownDescription: '%html.customData.desc%',
default: [],
items: {
type: 'string',
},
scope: 'resource',
},
'html.format.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%html.format.enable.desc%',
},
'html.format.wrapLineLength': {
type: 'integer',
scope: 'resource',
default: 120,
description: '%html.format.wrapLineLength.desc%',
},
'html.format.unformatted': {
type: ['string', 'null'],
scope: 'resource',
default: 'wbr',
markdownDescription: '%html.format.unformatted.desc%',
},
'html.format.contentUnformatted': {
type: ['string', 'null'],
scope: 'resource',
default: 'pre,code,textarea',
markdownDescription: '%html.format.contentUnformatted.desc%',
},
'html.format.indentInnerHtml': {
type: 'boolean',
scope: 'resource',
default: false,
markdownDescription: '%html.format.indentInnerHtml.desc%',
},
'html.format.preserveNewLines': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.format.preserveNewLines.desc%',
},
'html.format.maxPreserveNewLines': {
type: ['number', 'null'],
scope: 'resource',
default: null,
markdownDescription: '%html.format.maxPreserveNewLines.desc%',
},
'html.format.indentHandlebars': {
type: 'boolean',
scope: 'resource',
default: false,
markdownDescription: '%html.format.indentHandlebars.desc%',
},
'html.format.endWithNewline': {
type: 'boolean',
scope: 'resource',
default: false,
description: '%html.format.endWithNewline.desc%',
},
'html.format.extraLiners': {
type: ['string', 'null'],
scope: 'resource',
default: 'head, body, /html',
markdownDescription: '%html.format.extraLiners.desc%',
},
'html.format.wrapAttributes': {
type: 'string',
scope: 'resource',
default: 'auto',
enum: [
'auto',
'force',
'force-aligned',
'force-expand-multiline',
'aligned-multiple',
'preserve',
'preserve-aligned',
],
enumDescriptions: [
'%html.format.wrapAttributes.auto%',
'%html.format.wrapAttributes.force%',
'%html.format.wrapAttributes.forcealign%',
'%html.format.wrapAttributes.forcemultiline%',
'%html.format.wrapAttributes.alignedmultiple%',
'%html.format.wrapAttributes.preserve%',
'%html.format.wrapAttributes.preservealigned%',
],
description: '%html.format.wrapAttributes.desc%',
},
'html.format.wrapAttributesIndentSize': {
type: ['number', 'null'],
scope: 'resource',
default: null,
description: '%html.format.wrapAttributesIndentSize.desc%',
},
'html.format.templating': {
type: ['boolean'],
scope: 'resource',
default: false,
description: '%html.format.templating.desc%',
},
'html.format.unformattedContentDelimiter': {
type: ['string'],
scope: 'resource',
default: '',
markdownDescription:
'%html.format.unformattedContentDelimiter.desc%',
},
'html.suggest.html5': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.suggest.html5.desc%',
},
'html.validate.scripts': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.validate.scripts%',
},
'html.validate.styles': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.validate.styles%',
},
'html.autoClosingTags': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.autoClosingTags%',
},
'html.hover.documentation': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.hover.documentation%',
},
'html.hover.references': {
type: 'boolean',
scope: 'resource',
default: true,
description: '%html.hover.references%',
},
'html.mirrorCursorOnMatchingTag': {
type: 'boolean',
scope: 'resource',
default: false,
description: '%html.mirrorCursorOnMatchingTag%',
deprecationMessage:
'%html.mirrorCursorOnMatchingTagDeprecationMessage%',
},
'html.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%html.trace.server.desc%',
},
},
},
configurationDefaults: {
'[html]': {
'editor.suggest.insertMode': 'replace',
},
'[handlebars]': {
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.html-data.json',
url: 'https://raw.githubusercontent.com/microsoft/vscode-html-languageservice/master/docs/customData.schema.json',
},
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
workerMain: 'client/dist/browser/htmlClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'HTML Language Features',
description: 'Provides rich language support for HTML and Handlebar files',
'html.customData.desc':
'A list of relative file paths pointing to JSON files following the [custom data format](https://github.com/microsoft/vscode-html-languageservice/blob/master/docs/customData.md).\n\nVS Code loads custom data on startup to enhance its HTML support for the custom HTML tags, attributes and attribute values you specify in the JSON files.\n\nThe file paths are relative to workspace and only workspace folder settings are considered.',
'html.format.enable.desc': 'Enable/disable default HTML formatter.',
'html.format.wrapLineLength.desc':
'Maximum amount of characters per line (0 = disable).',
'html.format.unformatted.desc':
"List of tags, comma separated, that shouldn't be reformatted. `null` defaults to all tags listed at https://www.w3.org/TR/html5/dom.html#phrasing-content.",
'html.format.contentUnformatted.desc':
"List of tags, comma separated, where the content shouldn't be reformatted. `null` defaults to the `pre` tag.",
'html.format.indentInnerHtml.desc':
'Indent `<head>` and `<body>` sections.',
'html.format.preserveNewLines.desc':
'Controls whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.',
'html.format.maxPreserveNewLines.desc':
'Maximum number of line breaks to be preserved in one chunk. Use `null` for unlimited.',
'html.format.indentHandlebars.desc':
'Format and indent `{{#foo}}` and `{{/foo}}`.',
'html.format.endWithNewline.desc': 'End with a newline.',
'html.format.extraLiners.desc':
'List of tags, comma separated, that should have an extra newline before them. `null` defaults to `"head, body, /html"`.',
'html.format.wrapAttributes.desc': 'Wrap attributes.',
'html.format.wrapAttributes.auto':
'Wrap attributes only when line length is exceeded.',
'html.format.wrapAttributes.force': 'Wrap each attribute except first.',
'html.format.wrapAttributes.forcealign':
'Wrap each attribute except first and keep aligned.',
'html.format.wrapAttributes.forcemultiline': 'Wrap each attribute.',
'html.format.wrapAttributes.alignedmultiple':
'Wrap when line length is exceeded, align attributes vertically.',
'html.format.wrapAttributes.preserve': 'Preserve wrapping of attributes',
'html.format.wrapAttributes.preservealigned':
'Preserve wrapping of attributes but align.',
'html.format.templating.desc':
'Honor django, erb, handlebars and php templating language tags.',
'html.format.unformattedContentDelimiter.desc':
'Keep text content together between this string.',
'html.format.wrapAttributesIndentSize.desc':
"Alignment size when using 'force aligned' and 'aligned multiple' in `#html.format.wrapAttributes#` or `null` to use the default indent size.",
'html.suggest.html5.desc':
'Controls whether the built-in HTML language support suggests HTML5 tags, properties and values.',
'html.trace.server.desc':
'Traces the communication between VS Code and the HTML language server.',
'html.validate.scripts':
'Controls whether the built-in HTML language support validates embedded scripts.',
'html.validate.styles':
'Controls whether the built-in HTML language support validates embedded styles.',
'html.autoClosingTags': 'Enable/disable autoclosing of HTML tags.',
'html.mirrorCursorOnMatchingTag':
'Enable/disable mirroring cursor on matching HTML tag.',
'html.mirrorCursorOnMatchingTagDeprecationMessage':
'Deprecated in favor of `editor.linkedEditing`',
'html.hover.documentation':
'Show tag and attribute documentation in hover.',
'html.hover.references': 'Show references to MDN in hover.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,35 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'ide-dark-theme',
version: '2.4.0',
},
packageJSON: {
publisher: 'kaitian',
name: 'ide-dark-theme',
version: '2.4.0',
displayName: 'IDE UI Theme',
description: 'IDE UI Theme',
contributes: {
themes: [
{
id: 'opensumi-dark',
label: 'OpenSumi Dark',
uiTheme: 'vs-dark',
path: './themes/dark/plus.json',
},
{
id: 'opensumi-light',
label: 'OpenSumi Light',
uiTheme: 'vs',
path: './themes/light/plus.json',
},
],
},
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
webAssets: [],
mode: 'public',
};

View File

@ -0,0 +1,147 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'json-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'json-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icons/json.png',
activationEvents: ['onLanguage:json', 'onLanguage:jsonc'],
kaitianContributes: {
workerMain: 'client/dist/browser/jsonClientMain.js',
},
contributes: {
configuration: {
id: 'json',
order: 20,
type: 'object',
title: 'JSON',
properties: {
'json.schemas': {
type: 'array',
scope: 'resource',
description: '%json.schemas.desc%',
items: {
type: 'object',
default: {
fileMatch: ['/myfile'],
url: 'schemaURL',
},
properties: {
url: {
type: 'string',
default: '/user.schema.json',
description: '%json.schemas.url.desc%',
},
fileMatch: {
type: 'array',
items: {
type: 'string',
default: 'MyFile.json',
description: '%json.schemas.fileMatch.item.desc%',
},
minItems: 1,
description: '%json.schemas.fileMatch.desc%',
},
schema: {
$ref: 'http://json-schema.org/draft-07/schema#',
description: '%json.schemas.schema.desc%',
},
},
},
},
'json.format.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%json.format.enable.desc%',
},
'json.trace.server': {
type: 'string',
scope: 'window',
enum: ['off', 'messages', 'verbose'],
default: 'off',
description: '%json.tracing.desc%',
},
'json.colorDecorators.enable': {
type: 'boolean',
scope: 'window',
default: true,
description: '%json.colorDecorators.enable.desc%',
deprecationMessage:
'%json.colorDecorators.enable.deprecationMessage%',
},
'json.maxItemsComputed': {
type: 'number',
default: 5000,
description: '%json.maxItemsComputed.desc%',
},
'json.schemaDownload.enable': {
type: 'boolean',
default: true,
description: '%json.enableSchemaDownload.desc%',
tags: ['usesOnlineServices'],
},
},
},
configurationDefaults: {
'[json]': {
'editor.quickSuggestions': {
strings: true,
},
'editor.suggest.insertMode': 'replace',
},
'[jsonc]': {
'editor.quickSuggestions': {
strings: true,
},
'editor.suggest.insertMode': 'replace',
},
},
jsonValidation: [
{
fileMatch: '*.schema.json',
url: 'http://json-schema.org/draft-07/schema#',
},
],
workerMain: 'client/dist/browser/jsonClientMain.js',
},
},
defaultPkgNlsJSON: {
displayName: 'JSON Language Features',
description: 'Provides rich language support for JSON files.',
'json.schemas.desc':
'Associate schemas to JSON files in the current project',
'json.schemas.url.desc':
'A URL to a schema or a relative path to a schema in the current directory',
'json.schemas.fileMatch.desc':
"An array of file patterns to match against when resolving JSON files to schemas. `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern.",
'json.schemas.fileMatch.item.desc':
"A file pattern that can contain '*' to match against when resolving JSON files to schemas.",
'json.schemas.schema.desc':
'The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.',
'json.format.enable.desc': 'Enable/disable default JSON formatter',
'json.tracing.desc':
'Traces the communication between VS Code and the JSON language server.',
'json.colorDecorators.enable.desc': 'Enables or disables color decorators',
'json.colorDecorators.enable.deprecationMessage':
'The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.',
'json.schemaResolutionErrorMessage': 'Unable to resolve schema.',
'json.clickToRetry': 'Click to retry.',
'json.maxItemsComputed.desc':
'The maximum number of outline symbols and folding regions computed (limited for performance reasons).',
'json.maxItemsExceededInformation.desc':
'Show notification when exceeding the maximum number of outline symbols and folding regions.',
'json.enableSchemaDownload.desc':
'When enabled, JSON schemas can be fetched from http and https locations.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

View File

@ -0,0 +1,346 @@
module.exports = {
extension: {
publisher: 'alex-ext-public',
name: 'markdown-language-features-worker',
version: '1.53.0-patch.1',
},
packageJSON: {
name: 'markdown-language-features-worker',
publisher: 'alex',
version: '1.53.0-patch.1',
displayName: '%displayName%',
description: '%description%',
icon: 'icon.png',
activationEvents: [
'onLanguage:markdown',
'onCommand:markdown.preview.toggleLock',
'onCommand:markdown.preview.refresh',
'onCommand:markdown.showPreview',
'onCommand:markdown.showPreviewToSide',
'onCommand:markdown.showLockedPreviewToSide',
'onCommand:markdown.showSource',
'onCommand:markdown.showPreviewSecuritySelector',
'onCommand:markdown.api.render',
'onWebviewPanel:markdown.preview',
'onCustomEditor:vscode.markdown.preview.editor',
],
kaitianContributes: {
workerMain: './dist/browser/extension.js',
},
contributes: {
commands: [
{
command: 'markdown.showPreview',
title: '%markdown.preview.title%',
category: 'Markdown',
icon: {
light: './media/preview-light.svg',
dark: './media/preview-dark.svg',
},
},
{
command: 'markdown.showPreviewToSide',
title: '%markdown.previewSide.title%',
category: 'Markdown',
icon: '$(open-preview)',
},
{
command: 'markdown.showLockedPreviewToSide',
title: '%markdown.showLockedPreviewToSide.title%',
category: 'Markdown',
icon: '$(open-preview)',
},
{
command: 'markdown.showSource',
title: '%markdown.showSource.title%',
category: 'Markdown',
icon: '$(go-to-file)',
},
{
command: 'markdown.showPreviewSecuritySelector',
title: '%markdown.showPreviewSecuritySelector.title%',
category: 'Markdown',
},
{
command: 'markdown.preview.refresh',
title: '%markdown.preview.refresh.title%',
category: 'Markdown',
},
{
command: 'markdown.preview.toggleLock',
title: '%markdown.preview.toggleLock.title%',
category: 'Markdown',
},
],
menus: {
'editor/title': [
{
command: 'markdown.showPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
alt: 'markdown.showPreview',
group: 'navigation',
},
{
command: 'markdown.showSource',
when: 'markdownPreviewFocus',
group: 'navigation',
},
{
command: 'markdown.preview.refresh',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
{
command: 'markdown.preview.toggleLock',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'markdownPreviewFocus',
group: '1_markdown',
},
],
'explorer/context': [
{
command: 'markdown.showPreview',
when: 'resourceLangId == markdown',
group: 'navigation',
},
],
'editor/title/context': [
{
command: 'markdown.showPreview',
when: 'resourceLangId == markdown',
group: '1_open',
},
],
commandPalette: [
{
command: 'markdown.showPreview',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showLockedPreviewToSide',
when: 'editorLangId == markdown && !notebookEditorFocused',
group: 'navigation',
},
{
command: 'markdown.showSource',
when: 'markdownPreviewFocus',
group: 'navigation',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.showPreviewSecuritySelector',
when: 'markdownPreviewFocus',
},
{
command: 'markdown.preview.toggleLock',
when: 'markdownPreviewFocus',
},
{
command: 'markdown.preview.refresh',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.preview.refresh',
when: 'markdownPreviewFocus',
},
],
},
keybindings: [
{
command: 'markdown.showPreview',
key: 'shift+ctrl+v',
mac: 'shift+cmd+v',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
{
command: 'markdown.showPreviewToSide',
key: 'ctrl+k v',
mac: 'cmd+k v',
when: 'editorLangId == markdown && !notebookEditorFocused',
},
],
configuration: {
type: 'object',
title: 'Markdown',
order: 20,
properties: {
'markdown.styles': {
type: 'array',
items: {
type: 'string',
},
default: [],
description: '%markdown.styles.dec%',
scope: 'resource',
},
'markdown.preview.breaks': {
type: 'boolean',
default: false,
description: '%markdown.preview.breaks.desc%',
scope: 'resource',
},
'markdown.preview.linkify': {
type: 'boolean',
default: true,
description: '%markdown.preview.linkify%',
scope: 'resource',
},
'markdown.preview.fontFamily': {
type: 'string',
default:
"-apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif",
description: '%markdown.preview.fontFamily.desc%',
scope: 'resource',
},
'markdown.preview.fontSize': {
type: 'number',
default: 14,
description: '%markdown.preview.fontSize.desc%',
scope: 'resource',
},
'markdown.preview.lineHeight': {
type: 'number',
default: 1.6,
description: '%markdown.preview.lineHeight.desc%',
scope: 'resource',
},
'markdown.preview.scrollPreviewWithEditor': {
type: 'boolean',
default: true,
description: '%markdown.preview.scrollPreviewWithEditor.desc%',
scope: 'resource',
},
'markdown.preview.markEditorSelection': {
type: 'boolean',
default: true,
description: '%markdown.preview.markEditorSelection.desc%',
scope: 'resource',
},
'markdown.preview.scrollEditorWithPreview': {
type: 'boolean',
default: true,
description: '%markdown.preview.scrollEditorWithPreview.desc%',
scope: 'resource',
},
'markdown.preview.doubleClickToSwitchToEditor': {
type: 'boolean',
default: true,
description: '%markdown.preview.doubleClickToSwitchToEditor.desc%',
scope: 'resource',
},
'markdown.preview.openMarkdownLinks': {
type: 'string',
default: 'inPreview',
description:
'%configuration.markdown.preview.openMarkdownLinks.description%',
scope: 'resource',
enum: ['inPreview', 'inEditor'],
enumDescriptions: [
'%configuration.markdown.preview.openMarkdownLinks.inPreview%',
'%configuration.markdown.preview.openMarkdownLinks.inEditor%',
],
},
'markdown.links.openLocation': {
type: 'string',
default: 'currentGroup',
description:
'%configuration.markdown.links.openLocation.description%',
scope: 'resource',
enum: ['currentGroup', 'beside'],
enumDescriptions: [
'%configuration.markdown.links.openLocation.currentGroup%',
'%configuration.markdown.links.openLocation.beside%',
],
},
'markdown.trace': {
type: 'string',
enum: ['off', 'verbose'],
default: 'off',
description: '%markdown.trace.desc%',
scope: 'window',
},
},
},
configurationDefaults: {
'[markdown]': {
'editor.wordWrap': 'on',
'editor.quickSuggestions': false,
},
},
jsonValidation: [
{
fileMatch: 'package.json',
url: './schemas/package.schema.json',
},
],
'markdown.previewStyles': [
'./media/markdown.css',
'./media/highlight.css',
],
'markdown.previewScripts': ['./media/index.js'],
workerMain: './dist/browser/extension.js',
},
},
defaultPkgNlsJSON: {
displayName: 'Markdown Language Features',
description: 'Provides rich language support for Markdown.',
'markdown.preview.breaks.desc':
"Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for newlines inside paragraphs.",
'markdown.preview.linkify':
'Enable or disable conversion of URL-like text to links in the markdown preview.',
'markdown.preview.doubleClickToSwitchToEditor.desc':
'Double click in the markdown preview to switch to the editor.',
'markdown.preview.fontFamily.desc':
'Controls the font family used in the markdown preview.',
'markdown.preview.fontSize.desc':
'Controls the font size in pixels used in the markdown preview.',
'markdown.preview.lineHeight.desc':
'Controls the line height used in the markdown preview. This number is relative to the font size.',
'markdown.preview.markEditorSelection.desc':
'Mark the current editor selection in the markdown preview.',
'markdown.preview.scrollEditorWithPreview.desc':
'When a markdown preview is scrolled, update the view of the editor.',
'markdown.preview.scrollPreviewWithEditor.desc':
'When a markdown editor is scrolled, update the view of the preview.',
'markdown.preview.title': 'Open Preview',
'markdown.previewSide.title': 'Open Preview to the Side',
'markdown.showLockedPreviewToSide.title': 'Open Locked Preview to the Side',
'markdown.showSource.title': 'Show Source',
'markdown.styles.dec':
"A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
'markdown.showPreviewSecuritySelector.title':
'Change Preview Security Settings',
'markdown.trace.desc': 'Enable debug logging for the markdown extension.',
'markdown.preview.refresh.title': 'Refresh Preview',
'markdown.preview.toggleLock.title': 'Toggle Preview Locking',
'configuration.markdown.preview.openMarkdownLinks.description':
'Controls how links to other markdown files in the markdown preview should be opened.',
'configuration.markdown.preview.openMarkdownLinks.inEditor':
'Try to open links in the editor',
'configuration.markdown.preview.openMarkdownLinks.inPreview':
'Try to open links in the markdown preview',
'configuration.markdown.links.openLocation.description':
'Controls where links in markdown files should be opened.',
'configuration.markdown.links.openLocation.currentGroup':
'Open links in the active editor group.',
'configuration.markdown.links.openLocation.beside':
'Open links beside the active editor.',
},
pkgNlsJSON: {},
nlsList: [],
extendConfig: {},
mode: 'public',
};

File diff suppressed because it is too large Load Diff

59
src/forge/Newfile/ide-plugin.js Executable file
View File

@ -0,0 +1,59 @@
export const ExtensionCommand={
toggleBlame : 'code.blame.toggleBlame',
linkToCommit : 'code.blame.linktocommit',
onActive : 'code.blame.extension.active',
setPerference : 'code.blame.setPerference',
}
export default class IDEPlugin {
// // @ts-ignore
// commands: IPluginAPI['commands'];
// onActivate: () => void;
// linkToCommit: (commitId: string) => void;
/**
* 插件 ID用于唯一标识插件
*/
PLUGIN_ID = 'antcode-communication';
constructor(onActive, linkToCommit) {
this.onActivate = onActive;
this.linkToCommit = linkToCommit;
}
/**
* 激活插件
*/
activate = (ctx) => {
const { commands, context } = ctx;
this.commands = commands;
context.subscriptions.push(
commands.registerCommand(ExtensionCommand.onActive, () => {
this.onActivate();
}),
commands.registerCommand(ExtensionCommand.linkToCommit, (params) => {
const { commitId } = params;
this.linkToCommit(commitId);
})
);
};
/**
* 注销插件可在此时机清理副作用
*/
deactivate() {}
/**
* 修改配置项
* @param name 配置项名称
* @param value
* @param global 是否全局生效
*/
setPerference(name, value, global) {
this.commands.executeCommand(
ExtensionCommand.setPerference,
name,
value,
!!global
);
}
}

View File

@ -1,11 +1,11 @@
import React, { Component } from "react";
import {UnControlled as CodeMirror} from 'react-codemirror2';
import React, { Component, Fragment } from "react";
import { UnControlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/addon/selection/active-line.js';
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/clike/clike';
import 'codemirror/mode/css/css';
import UserSubmitComponent from "./UserSubmitComponent";
import CloudIDE from "./cloudIDE";
import "./index.css";
import "./editor.css";
@ -15,26 +15,28 @@ class m_editor extends Component {
super(props);
this.state = {
editorValue: this.props.content,
changeValue:this.props.content,
prevHeight:0
changeValue: this.props.content,
prevHeight: 0,
};
}
componentDidUpdate=(prevProps)=>{
if(prevProps && this.props && this.props.content !== prevProps.content){
componentDidUpdate = (prevProps) => {
if (prevProps && this.props && this.props.content !== prevProps.content) {
this.setState({
editorValue:this.props.content
editorValue: this.props.content
})
}
}
changeEditor = (editorValue,data) => {
changeEditor = (editorValue, data) => {
this.setState({
changeValue:editorValue.getValue(),
changeValue: editorValue.getValue(),
});
};
render() {
const { editorValue , changeValue } = this.state;
const { readOnly, editorType, currentBranch , descName , checkName } = this.props;
const { editorValue, changeValue } = this.state;
const { readOnly, editorType, currentBranch, descName, checkName, detail, match: { params },filepath,ideTheme } = this.props;
console.log('params')
console.log(params)
const editor_options = {
lineNumbers: "on",
lineWrapping: true, //强制换行
@ -42,7 +44,7 @@ class m_editor extends Component {
lineHeight: 24,
renderLineHighlight: "line",
revealHorizontalRightPadding: 5,
placeholder:"请输入内容",
placeholder: "请输入内容",
readOnly: readOnly,
cursorStyle: readOnly ? "underline-thin" : "line",
folding: true,
@ -50,24 +52,29 @@ class m_editor extends Component {
automaticLayout: true, // 自适应布局
overviewRulerBorder: false, // 不要滚动条的边框
scrollBeyondLastLine: false, // 取消代码后面一大段空白
styleActiveLine:true,//光标代码高亮
styleActiveLine: true,//光标代码高亮
minimap: {
// 不要小地图
enabled: false,
},
};
let download_url = detail && detail.download_url;
return (
<React.Fragment>
<Fragment>
<div className="editorBorderBox">
<CodeMirror
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
/>
{!readOnly ?
<CodeMirror
placeholder="请输入内容"
value={editorValue}
options={editor_options}
onChange={this.changeEditor}
/> :
<CloudIDE download_url={download_url} params={params} filepath={filepath&&filepath.startsWith('/')?filepath.slice(1):filepath}/>
}
</div>
{!readOnly && (
<div className="editorBorderSubmitBox" style={{padding:"20px"}}>
<div className="editorBorderSubmitBox" style={{ padding: "20px" }}>
<UserSubmitComponent
{...this.props}
{...this.state}
@ -78,9 +85,9 @@ class m_editor extends Component {
descName={descName}
checkName={checkName}
></UserSubmitComponent>
</div>
</div>
)}
</React.Fragment>
</Fragment>
);
}
}

View File

@ -1,28 +1,28 @@
.noticeMenu{
padding:0px 30px;
.noticeMenu {
padding: 0px 30px;
display: flex;
border-bottom: 1px solid #eee;
li{
li {
font-size: 16px;
padding:0px;
margin-right:30px;
padding: 0px;
margin-right: 30px;
height: 70px;
line-height: 70px;
position: relative;
transform: none;
a{
a {
display: flex;
}
&.active a span{
color: #1890ff;
&.active a span {
color: $primary-color;
}
.unNum{
.unNum {
color: #d38900;
font-size: 12px;
border-radius: 13px;
height: 16px;
line-height: 16px;
padding:0px 4px;
padding: 0px 4px;
min-width: 23px;
text-align: center;
background-color: #ffe4b3;
@ -32,30 +32,31 @@
}
}
}
.notifyList{
padding:0px 30px;
li{
.notifyList {
padding: 0px 30px;
li {
display: flex;
border-bottom: 1px solid #eee;
padding:20px 0px;
.notifyImg{
padding: 20px 0px;
.notifyImg {
width: 48px;
height: 48px;
border-radius: 50%;
margin-right: 15px;
}
.notifyFlex{
flex:1;
p{
margin:0px;
.notifyFlex {
flex: 1;
p {
margin: 0px;
max-width: 660px;
}
.notifyInfos{
.notifyInfos {
margin-bottom: 8px;
}
}
&:last-child{
&:last-child {
border-bottom: none;
}
}
}
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import { Modal } from 'antd';
import './order.css';
import './order.scss';
function AmplifyImg(props) {
return (

View File

@ -8,7 +8,7 @@ import Attachments from "../Upload/attachment";
import RenderHtml from "../../components/render-html";
import Comments from "../comments/comments";
import Claims from "../claims/claims"
import "./order.css";
import "./order.scss";
class Detail extends Component {
constructor(props) {

Some files were not shown because too many files have changed in this diff Show More