Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Z
zeroing-editor
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
劳工
zeroing-editor
Commits
7de225e4
Commit
7de225e4
authored
May 27, 2020
by
任建锋
Browse files
Options
Browse Files
Download
Plain Diff
--
parents
652f1ad0
8f86fbae
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
271 additions
and
81 deletions
+271
-81
bottom.png
src/assets/bottom.png
+0
-0
center.png
src/assets/center.png
+0
-0
top.png
src/assets/top.png
+0
-0
PropsEditor.vue
src/components/PropsEditor.vue
+12
-13
NumberInput.vue
src/components/inputs/NumberInput.vue
+60
-17
zh-CN.json
src/locales/zh-CN.json
+42
-36
editor.js
src/store/modules/editor.js
+1
-1
project.js
src/store/modules/project.js
+15
-2
template.js
src/template.js
+2
-0
behavior.scss
src/themes/light/behavior.scss
+8
-0
views.scss
src/themes/light/views.scss
+22
-0
properties.js
src/utils/properties.js
+18
-0
Views.vue
src/views/Editor/Views.vue
+12
-6
SelectDesignModeDialog.vue
src/views/Editor/Views/SelectDesignModeDialog.vue
+65
-0
editControlView.vue
src/views/Editor/components/editControlView.vue
+8
-0
viewsTree.vue
src/views/Editor/components/viewsTree.vue
+5
-5
ProjectConflictResolveDialog.vue
src/views/Editor/dialogs/ProjectConflictResolveDialog.vue
+1
-1
No files found.
src/assets/bottom.png
0 → 100644
View file @
7de225e4
769 Bytes
src/assets/center.png
0 → 100644
View file @
7de225e4
710 Bytes
src/assets/top.png
0 → 100644
View file @
7de225e4
752 Bytes
src/components/PropsEditor.vue
View file @
7de225e4
...
...
@@ -15,13 +15,14 @@
@
cmd-prop-change=
"onCmdPropChanged"
>
<component
:is=
"getInput(property)"
:container=
"data"
:value=
"data[key]"
:propertyName=
"key"
:property=
"property"
:editable=
"editable"
@
input=
"onInput"
:is=
"getInput(property)"
:container=
"data"
:value=
"data[key]"
:propertyName=
"key"
:property=
"property"
:editable=
"editable"
:config =
"property.input"
@
input=
"onInput"
/>
</component>
</el-form>
...
...
@@ -83,9 +84,7 @@
SampleInputWrapper
,
},
data
()
{
return
{
}
return
{}
},
props
:
{
labelWidth
:
{
...
...
@@ -112,8 +111,8 @@
},
},
watch
:
{
data
(
v
){
console
.
log
(
"data变更"
,
v
)
data
(
v
)
{
}
},
methods
:
{
...
...
@@ -129,7 +128,7 @@
}
this
.
$emit
(
'input'
,
value
,
container
,
propName
,
oldValue
);
},
onCmdPropChanged
(
value
,
container
,
propName
,
oldValue
){
onCmdPropChanged
(
value
,
container
,
propName
,
oldValue
)
{
this
.
$emit
(
'input'
,
value
,
container
,
propName
,
oldValue
);
},
}
...
...
src/components/inputs/NumberInput.vue
View file @
7de225e4
<
template
>
<el-input-number
:disabled=
"!editable"
:value=
"editValue"
@
change=
"onInput"
controls-position=
"right"
:placeholder=
"defaultValue"
></el-input-number>
<
template
>
<div
style=
"flex: 1; display: flex;"
>
<el-input-number
:disabled=
"!editable"
:value=
"editValue"
@
change=
"onInput"
controls-position=
"right"
:placeholder=
"defaultValue"
>
</el-input-number>
<span
@
click=
"onClickPercent"
v-if=
"config && config.percent"
style=
"position: absolute; right: 42px; cursor: pointer"
:class=
"
{'percent-on': isPercent, 'percent-off': !isPercent}">%
</span>
</div>
</
template
>
<
script
>
...
...
@@ -12,27 +16,40 @@
export
default
{
name
:
"NumberInput"
,
components
:
{
LinkableInputWrapper
,
CmdInputWrapper
},
props
:
[
'value'
,
'container'
,
'property'
,
'propertyName'
,
'editable'
],
data
(){
props
:
[
'value'
,
'container'
,
'property'
,
'propertyName'
,
'editable'
,
'config'
],
data
()
{
return
{
defaultValue
:
this
.
hasPrefixedPropertyName
?
this
.
value
+
""
:
getInputDefaultValue
(
this
.
property
)
,
isPercent
:
false
,
editValue
:
null
}
},
computed
:
{
/*editValue() {
let v = this.value;
if (v === undefined) {
this.isPercent = false;
return this.property.default?this.property.default:undefined;
} else {
this.isPercent = typeof v === 'string';
if(typeof v === 'string'){
v = parseInt(v);
}
return v;
}
},*/
defaultValue
()
{
return
getInputDefaultValue
(
this
.
property
);
},
},
mounted
(){
this
.
updateValue
()
},
watch
:
{
value
:
function
(
v
)
{
this
.
updateValue
()
},
},
methods
:
{
updateValue
()
{
if
(
this
.
container
.
hasOwnProperty
(
'_'
+
this
.
propertyName
)){
if
(
this
.
container
[
'_'
+
this
.
propertyName
]){
this
.
editValue
=
this
.
container
[
'_'
+
this
.
propertyName
];
}
else
{
this
.
editValue
=
undefined
;
}
...
...
@@ -43,16 +60,42 @@
this
.
editValue
=
undefined
;
}
}
else
{
this
.
editValue
=
this
.
value
===
undefined
?
this
.
property
.
default
:
this
.
value
;
let
v
=
this
.
value
;
if
(
v
===
undefined
)
{
this
.
isPercent
=
false
;
this
.
editValue
=
this
.
property
.
default
?
this
.
property
.
default
:
undefined
;
}
else
{
this
.
isPercent
=
typeof
v
===
'string'
;
if
(
typeof
v
===
'string'
){
v
=
parseInt
(
v
);
}
this
.
editValue
=
v
;
}
//this.editValue = this.value === undefined ? this.property.default : this.value;
}
},
onInput
(
v
)
{
console
.
log
(
"v set"
,
v
)
if
(
v
!==
this
.
value
){
if
(
v
!==
this
.
value
)
{
this
.
$emit
(
'input'
,
v
,
this
.
container
,
this
.
propertyName
,
this
.
value
);
}
}
},
onClickPercent
()
{
if
(
this
.
isPercent
){
//设置非百分比
this
.
onInput
(
parseInt
(
this
.
value
))
}
else
{
//设置成百分比
if
(
!
this
.
value
&&
this
.
value
!==
0
){
this
.
onInput
(
undefined
)
}
else
{
this
.
onInput
(
this
.
value
+
'%'
)
}
}
},
},
watch
:{
value
(
v
){
this
.
updateValue
()
}
}
}
</
script
>
...
...
src/locales/zh-CN.json
View file @
7de225e4
...
...
@@ -2,8 +2,8 @@
"Alert"
:
"提示"
,
"Confirm"
:
"确认"
,
"Cancel"
:
"取消"
,
"Yes"
:
"是"
,
"No"
:
"否"
,
"Yes"
:
"是"
,
"No"
:
"否"
,
"Close"
:
"关闭"
,
"Refresh"
:
"刷新"
,
"Still Close"
:
"直接关闭"
,
...
...
@@ -18,8 +18,8 @@
"Remark"
:
"备注"
,
"Assets"
:
"素材"
,
"Version"
:
"版本"
,
"Paste same level"
:
"粘贴(同级)"
,
"Paste child"
:
"粘贴(子级)"
,
"Paste same level"
:
"粘贴(同级)"
,
"Paste child"
:
"粘贴(子级)"
,
"Exit"
:
"退出"
,
"Apply"
:
"应用"
,
"Props"
:
"属性"
,
...
...
@@ -133,8 +133,8 @@
"Env constant"
:
"自定义常量"
,
"Custom module"
:
"自定义模块"
,
"Package manager"
:
"包管理"
,
"Projectx config"
:
"星速台配置"
,
"Auth manager"
:
"权限管理"
,
"Projectx config"
:
"星速台配置"
,
"Auth manager"
:
"权限管理"
,
"Custom module asset mapping"
:
"自定义模块素材映射"
,
"Copy template to clipboard"
:
"复制模板到粘贴板"
,
"Copied process to clipboard"
:
"复制过程到粘贴板"
,
...
...
@@ -179,8 +179,8 @@
"Are you sure to delete this asset?"
:
"确定删除这个素材吗"
,
"Are you sure to combine those assets?"
:
"确定合并这些素材吗"
,
"Are you sure to delete all assets"
:
"确定删除全部素材吗"
,
"Are you sure to delete this item"
:
"确定删除这一项吗"
,
"Are you sure to delete this user auth"
:
"确定删除这一用户权限"
,
"Are you sure to delete this item"
:
"确定删除这一项吗"
,
"Are you sure to delete this user auth"
:
"确定删除这一用户权限"
,
"Rename asset"
:
"重命名素材"
,
"Input asset name"
:
"输入素材名"
,
"Invalid asset name"
:
"无效的素材名"
,
...
...
@@ -257,6 +257,7 @@
"Save behavior successfully"
:
"保存行为成功"
,
"The main editor is closed and cannot be saved"
:
"主编辑器已关闭,无法保存"
,
"A behavior is being edited. Please save it first"
:
"有行为正在编辑,请先保存"
,
"Design cut mode"
:
"设计稿裁剪模式"
,
"eventGroup"
:
{
"in"
:
"接收"
,
"out"
:
"派发"
...
...
@@ -269,35 +270,35 @@
}
},
"details"
:
{
"label"
:
"详情"
"label"
:
"详情"
},
"preview-fast"
:
{
"label"
:
"预览"
"label"
:
"预览"
},
"search"
:
{
"label"
:
"搜索"
"label"
:
"搜索"
},
"pack"
:
{
"label"
:
"打包"
,
"label"
:
"打包"
,
"sub"
:
{
"pack-debug-mode"
:
"调试打包"
,
"pack-manager"
:
"打包管理"
}
},
"mock"
:
{
"label"
:
"Mock"
"label"
:
"Mock"
},
"px-skin-editor"
:
{
"label"
:
"皮肤"
"label"
:
"皮肤"
},
"exit"
:
{
"label"
:
"退出"
"label"
:
"退出"
},
"undo"
:
{
"icon"
:
"icon-undo"
"icon"
:
"icon-undo"
},
"redo"
:
{
"icon"
:
"icon-redo"
"icon"
:
"icon-redo"
}
},
"view_node_menu"
:
{
...
...
@@ -423,38 +424,43 @@
"simple"
:
true
},
"dependencies"
:
{
"label"
:
"依赖"
,
"simple"
:
true
"label"
:
"依赖"
,
"simple"
:
true
},
"dataMapping"
:
{
"label"
:
"数据映射"
,
"key"
:
"name"
,
"name"
:
"name"
"label"
:
"数据映射"
,
"key"
:
"name"
,
"name"
:
"name"
},
"customs"
:
{
"label"
:
"自定义模块"
,
"key"
:
"id"
,
"name"
:
"id"
"label"
:
"自定义模块"
,
"key"
:
"id"
,
"name"
:
"id"
},
"mock"
:
{
"label"
:
"Mock"
,
"key"
:
"path"
,
"label"
:
"Mock"
,
"key"
:
"path"
,
"name"
:
"path"
},
"views"
:
{
"label"
:
"视图"
,
"key"
:
"uuid"
,
"name"
:
"name"
"label"
:
"视图"
,
"key"
:
"uuid"
,
"name"
:
"name"
},
"assets"
:
{
"label"
:
"素材"
,
"key"
:
"uuid"
,
"name"
:
"name"
"label"
:
"素材"
,
"key"
:
"uuid"
,
"name"
:
"name"
},
"processes"
:
{
"label"
:
"过程"
,
"key"
:
"id"
,
"name"
:
"name"
"label"
:
"过程"
,
"key"
:
"id"
,
"name"
:
"name"
}
},
"designCutModes"
:
{
"top"
:
"顶部裁剪"
,
"center"
:
"上下裁剪"
,
"bottom"
:
"底部裁剪"
}
}
\ No newline at end of file
src/store/modules/editor.js
View file @
7de225e4
...
...
@@ -17,7 +17,7 @@ export const editorStore = {
state
:
{
initialized
:
false
,
name
:
'Zeroing Editor'
,
version
:
'0.
2.1
'
,
version
:
'0.
3.0
'
,
templates
:
{
builtin
:
[
'blank'
],
custom
:
[],
...
...
src/store/modules/project.js
View file @
7de225e4
...
...
@@ -31,6 +31,7 @@ import events from "@/global-events.js"
const
storeName
=
'project'
;
const
psStoreName
=
'pack-history'
;
const
offsetAll
=
172
;
const
defaultOptions
=
{
behaviorEditor
:
'builtin'
,
...
...
@@ -922,8 +923,20 @@ export const projectStore = {
scripts
:
_scripts
})
},
async
importPsd
({
commit
},
{
file
,
action
})
{
const
result
=
await
toZeroing
(
file
);
async
importPsd
({
commit
},
{
file
,
action
,
mode
})
{
let
offset
=
{};
switch
(
mode
){
case
'top'
:
offset
.
y
=
offsetAll
;
break
;
case
'center'
:
offset
.
y
=
offsetAll
/
2
;
break
;
case
'bottom'
:
break
;
}
const
result
=
await
toZeroing
(
file
,
{
offset
});
let
viewFile
=
new
File
([
result
],
'view.json'
);
const
{
view
,
assets
}
=
await
editorApi
.
uploadView
(
viewFile
);
switch
(
action
)
{
...
...
src/template.js
View file @
7de225e4
...
...
@@ -27,6 +27,8 @@ export const template =
overflow: hidden;
position: absolute;
background-color: white;
-webkit-touch-callout: none;
}
.game-container{
width: 100%;
...
...
src/themes/light/behavior.scss
View file @
7de225e4
...
...
@@ -501,6 +501,14 @@ $dock-pin-width: 9px;
.node-select-container
{
flex
:
1
;
}
.percent-on
{
color
:
$--color-primary
;
}
.percent-off
{
color
:
$--color-text-placeholder
;
}
}
.source-input-popover
{
...
...
src/themes/light/views.scss
View file @
7de225e4
...
...
@@ -55,4 +55,26 @@
}
}
}
}
.select-design-mode-dialog
{
.wrapper
{
display
:
flex
;
justify-content
:
space-around
;
padding
:
10px
;
.item
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
padding
:
10px
;
border-radius
:
10px
;
border
:
2px
solid
transparent
;
}
.item.selected
{
border-color
:
$--color-primary
;
}
}
}
\ No newline at end of file
src/utils/properties.js
View file @
7de225e4
...
...
@@ -22,10 +22,16 @@ export default {
width
:
{
alias
:
'宽度'
,
type
:
'number'
,
input
:
{
percent
:
false
,
},
},
height
:
{
alias
:
'高度'
,
type
:
'number'
,
input
:
{
percent
:
false
,
},
},
_width
:
{
alias
:
'[宽度]'
,
...
...
@@ -38,18 +44,30 @@ export default {
left
:
{
alias
:
'左边距'
,
type
:
'number'
,
input
:
{
percent
:
true
,
},
},
right
:
{
alias
:
'右边距'
,
type
:
'number'
,
input
:
{
percent
:
true
,
},
},
top
:
{
alias
:
'上边距'
,
type
:
'number'
,
input
:
{
percent
:
true
,
},
},
bottom
:
{
alias
:
'下边距'
,
type
:
'number'
,
input
:
{
percent
:
true
,
},
},
horizonCenter
:
{
alias
:
'水平居中偏移'
,
...
...
src/views/Editor/Views.vue
View file @
7de225e4
...
...
@@ -3,7 +3,7 @@
<div
class=
"container"
>
<div
class=
"header-bar"
>
<el-link
class=
"menu-item"
@
click=
"toAddView"
>
{{
$t
(
'Add'
)
}}
</el-link>
<el-link
@
click=
"
toImport(0
)"
>
{{
$t
(
'Import'
)
}}
</el-link>
<el-link
@
click=
"
onImportCommand('single'
)"
>
{{
$t
(
'Import'
)
}}
</el-link>
<el-dropdown
trigger=
"hover"
placement=
"top"
size=
"mini"
@
command=
"onImportCommand"
>
<el-link
icon=
"el-icon-arrow-down"
:underline=
"false"
></el-link>
<el-dropdown-menu
slot=
"dropdown"
>
...
...
@@ -69,6 +69,7 @@
</el-tree>
</el-scrollbar>
</div>
<select-design-mode-dialog
ref=
"selectDesignModeDialog"
@
change=
"onSelectDesignMode"
/>
</pane>
</
template
>
...
...
@@ -77,10 +78,11 @@
import
Pane
from
'../../components/Pane'
;
import
{
selectFile
}
from
"../../utils"
;
import
events
from
"../../global-events"
;
import
SelectDesignModeDialog
from
"./Views/SelectDesignModeDialog"
;
export
default
{
name
:
'Views'
,
components
:
{
Pane
},
components
:
{
SelectDesignModeDialog
,
Pane
},
data
()
{
return
{
filterText
:
''
,
...
...
@@ -298,22 +300,26 @@
.
catch
(()
=>
{
});
},
toImport
(
action
)
{
toImport
(
action
,
mode
)
{
selectFile
(
async
files
=>
{
events
.
$emit
(
'upload-indicator'
,
true
);
try
{
await
this
.
importPsd
({
file
:
files
[
0
],
action
,
mode
,
});
}
catch
(
e
)
{
}
events
.
$emit
(
'upload-indicator'
,
false
);
})
},
onImportCommand
(
command
)
{
onImportCommand
(
importType
)
{
this
.
$refs
.
selectDesignModeDialog
.
show
(
importType
);
},
onSelectDesignMode
(
mode
,
importType
){
let
action
;
switch
(
command
)
{
switch
(
importType
)
{
case
'single'
:
action
=
0
;
break
;
...
...
@@ -321,7 +327,7 @@
action
=
1
;
break
;
}
this
.
toImport
(
action
);
this
.
toImport
(
action
,
mode
);
},
onMoreMenu
(
command
,
data
,
node
)
{
switch
(
command
)
{
...
...
src/views/Editor/Views/SelectDesignModeDialog.vue
0 → 100644
View file @
7de225e4
<
template
>
<el-dialog
:title=
"$t('Design cut mode')"
:visible
.
sync=
"visible"
@
opened=
"onOpen"
:append-to-body=
"true"
custom-class=
"select-design-mode-dialog"
>
<div
class=
"wrapper"
>
<div
v-for=
"(modeName, mode, index) in designCutModes"
:key=
"index"
@
click=
"selectCutMode(mode)"
class=
"item"
:class=
"
{selected: designCutMode === mode}">
{{
modeName
}}
<img
:src=
"images[index]"
>
</div>
</div>
<div
slot=
"footer"
class=
"dialog-footer"
>
<div></div>
<div>
<el-button
size=
"mini"
@
click=
"onClose"
>
{{
$t
(
'Close'
)
}}
</el-button>
<el-button
size=
"mini"
@
click=
"onConfirm"
type=
"primary"
>
{{
$t
(
'Confirm'
)
}}
</el-button>
</div>
</div>
</el-dialog>
</
template
>
<
script
>
export
default
{
name
:
"SelectDesignModeDialog"
,
components
:
{},
data
()
{
const
designCutModes
=
this
.
$t
(
'designCutModes'
);
let
images
=
[];
let
designCutModeKeys
=
Object
.
keys
(
designCutModes
);
for
(
let
designCutModeKey
of
designCutModeKeys
)
{
images
.
push
(
require
(
`@/assets/
${
designCutModeKey
}
.png`
))
}
return
{
visible
:
false
,
designCutMode
:
'top'
,
designCutModes
,
images
,
}
},
methods
:
{
show
(
importType
)
{
this
.
importType
=
importType
;
this
.
visible
=
true
;
},
onConfirm
()
{
this
.
$emit
(
'change'
,
this
.
designCutMode
,
this
.
importType
);
this
.
visible
=
false
;
},
onClose
()
{
this
.
visible
=
false
;
},
onOpen
()
{
},
selectCutMode
(
mode
){
this
.
designCutMode
=
mode
;
}
}
}
</
script
>
<
style
scoped
>
</
style
>
\ No newline at end of file
src/views/Editor/components/editControlView.vue
View file @
7de225e4
...
...
@@ -322,6 +322,7 @@ export default {
horizonCenter
:
this
.
targetNode
.
horizonCenter
,
mouseChildren
:
this
.
targetNode
.
mouseChildren
,
verticalCenter
:
this
.
targetNode
.
verticalCenter
,
instanceType
:
this
.
targetNode
.
instanceType
,
source
:
this
.
targetNode
.
source
,
_width
:
this
.
targetNode
.
_width
,
_height
:
this
.
targetNode
.
_height
,
...
...
@@ -345,6 +346,11 @@ export default {
let
{
rotation
,
scaleX
,
scaleY
,
x
,
y
}
=
this
.
matrixToGroup
(
this
.
styleCatch
.
worldMatrix
)
console
.
log
(
"测试矩阵"
,
this
.
matrixToGroup
(
this
.
styleCatch
.
worldMatrix
))
if
(
this
.
styleCatch
.
instanceType
==
"Sprite"
&&!
this
.
styleCatch
.
source
){
scaleX
=
1
;
scaleY
=
1
;
}
let
result
=
{
width
:
`
${
(
this
.
styleCatch
.
imageWidth
?
this
.
styleCatch
.
imageWidth
:(
this
.
styleCatch
.
width
?
this
.
styleCatch
.
width
:
0
))
*
scaleX
}
px`
,
height
:
`
${
(
this
.
styleCatch
.
imageHeight
?
this
.
styleCatch
.
imageHeight
:(
this
.
styleCatch
.
height
?
this
.
styleCatch
.
height
:
0
))
*
scaleY
}
px`
,
...
...
@@ -417,6 +423,7 @@ export default {
horizonCenter
,
verticalCenter
,
mouseChildren
,
instanceType
,
source
,
_width
,
_height
,
...
...
@@ -444,6 +451,7 @@ export default {
bottom
,
horizonCenter
,
mouseChildren
,
instanceType
,
verticalCenter
,
source
,
_width
,
...
...
src/views/Editor/components/viewsTree.vue
View file @
7de225e4
...
...
@@ -54,14 +54,14 @@ export default {
return
styles
.
getComponentStyle
(
this
.
activeComponentCopy
,
this
.
project
,
this
.
componentList
,
true
);
},
position
()
{
let
_props
=
this
.
activeComponentCopy
.
properties
||
{};
let
{
x
,
y
,
width
,
height
}
=
this
.
activeComponentCopy
.
properties
||
{};
const
props
=
properties
.
node
.
props
;
// console.log('********', _props);
let
result
=
{
x
:
_props
.
x
||
props
.
x
.
default
,
y
:
_props
.
y
||
props
.
y
.
default
,
w
:
_props
.
width
||
props
.
width
.
default
,
h
:
_props
.
height
||
props
.
height
.
default
,
x
:
x
||
props
.
x
.
default
,
y
:
y
||
props
.
y
.
default
,
w
:
width
?
(
typeof
width
===
'string'
?
parseInt
(
width
)
:
width
)
:
props
.
width
.
default
,
h
:
height
?
(
typeof
height
===
'string'
?
parseInt
(
height
)
:
height
)
:
props
.
height
.
default
,
};
// console.log('####position', result);
...
...
src/views/Editor/dialogs/ProjectConflictResolveDialog.vue
View file @
7de225e4
...
...
@@ -92,7 +92,7 @@
this
.
visible
=
false
;
},
analyseCompare
(
remoteData
,
localData
)
{
let
remoteJson
=
JSON
.
parse
(
remoteData
)
;
let
remoteJson
=
remoteData
?
JSON
.
parse
(
remoteData
)
:
{}
;
let
localJson
=
JSON
.
parse
(
localData
);
this
.
localObj
=
localJson
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment