#3953 fix-3790

Merged
chenshihai merged 12 commits from fix-3790 into V20230410 1 year ago
  1. +44
    -3
      modules/markup/markdown/goldmark.go
  2. +28
    -10
      modules/markup/markdown/toc.go
  3. +1
    -1
      modules/markup/sanitizer.go
  4. +2
    -0
      modules/setting/setting.go
  5. +1
    -0
      public/img/holder.svg
  6. +3
    -5
      templates/repo/view_file.tmpl
  7. +42
    -0
      web_src/js/index.js
  8. +94
    -4
      web_src/less/_markdown.less

+ 44
- 3
modules/markup/markdown/goldmark.go View File

@@ -41,7 +41,8 @@ type ASTTransformer struct{}
func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
metaData := meta.GetItems(pc)
firstChild := node.FirstChild()
createTOC := false

createTOC := setting.Markdown.EnableToc
var toc = []Header{}
rc := &RenderConfig{
Meta: "",
@@ -154,6 +155,25 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
}
return ast.WalkContinue, nil
})
if node.HasChildren() {
children := make([]ast.Node, 0, node.ChildCount())
child := node.FirstChild()
for child != nil {
children = append(children, child)
child = child.NextSibling()
}
node.RemoveChildren(node)
contentNode := ast.NewDocument()
contentNode.SetAttributeString("class", []byte("markdown-content"))
for _, child := range children {

contentNode.AppendChild(contentNode, child)

}

node.AppendChild(node, contentNode)

}

if createTOC && len(toc) > 0 {
lang := rc.Lang
@@ -161,6 +181,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
lang = setting.Langs[0]
}
tocNode := createTOCNode(toc, lang)
firstChild = node.FirstChild()
if tocNode != nil {
node.InsertBefore(node, firstChild, tocNode)
}
@@ -248,7 +269,27 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Document)

if val, has := n.AttributeString("lang"); has {
var err error
if entering {
_, err = w.WriteString("<div")
if err == nil {
if n.Attributes() != nil {
html.RenderAttributes(w, n, html.GlobalAttributeFilter)
}

}
if err == nil {
_, err = w.WriteRune('>')
}
} else {
_, err = w.WriteString("</div>")
}

if err != nil {
return ast.WalkStop, err
}

/**if val, has := n.AttributeString("lang"); has {
var err error
if entering {
_, err = w.WriteString("<div")
@@ -265,7 +306,7 @@ func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.
if err != nil {
return ast.WalkStop, err
}
}
}*/

return ast.WalkContinue, nil
}


+ 28
- 10
modules/markup/markdown/toc.go View File

@@ -8,18 +8,24 @@ import (
"fmt"
"net/url"

"github.com/unknwon/i18n"
"github.com/yuin/goldmark/ast"
)

func createTOCNode(toc []Header, lang string) ast.Node {
details := NewDetails()
summary := NewSummary()

summary.AppendChild(summary, ast.NewString([]byte(i18n.Tr(lang, "toc"))))
details.AppendChild(details, summary)
sidebar := ast.NewDocument()
sidebar.SetAttributeString("class", []byte("markdown_catalog"))
scrollContainer := ast.NewDocument()
scrollContainer.SetAttributeString("class", []byte("scroll-container"))
toggleContainer := ast.NewDocument()
toggleContainer.SetAttributeString("class", []byte("toggle-container"))
toggleIcon := ast.NewDocument()
toggleIcon.SetAttributeString("class", []byte("icon ri-arrow-drop-left-line"))
container := ast.NewDocument()
container.SetAttributeString("class", []byte("container"))
ul := ast.NewList('-')
details.AppendChild(details, ul)
ul.SetAttributeString("class", []byte("markdown_toc"))
topul:=ul

currentLevel := 6
for _, header := range toc {
if header.Level < currentLevel {
@@ -28,22 +34,34 @@ func createTOCNode(toc []Header, lang string) ast.Node {
}
for _, header := range toc {
for currentLevel > header.Level {
ul = ul.Parent().(*ast.List)
ul = ul.Parent().Parent().(*ast.List)
currentLevel--
}
for currentLevel < header.Level {
newLi := ast.NewListItem(currentLevel * 2)
newLi.SetAttributeString("class", []byte("no-catalog-li"))
newL := ast.NewList('-')
ul.AppendChild(ul, newL)
newL.SetAttributeString("class", []byte("markdown_toc"))
newLi.AppendChild(newLi, newL)
ul.AppendChild(ul, newLi)
currentLevel++
ul = newL
}
li := ast.NewListItem(currentLevel * 2)
li.SetAttributeString("class", []byte("catalog-li"))
a := ast.NewLink()
a.Destination = []byte(fmt.Sprintf("#%s", url.PathEscape(header.ID)))
a.AppendChild(a, ast.NewString([]byte(header.Text)))
a.SetAttributeString("title", []byte(header.Text))
li.AppendChild(li, a)
ul.AppendChild(ul, li)
}
container.AppendChild(container,topul)
scrollContainer.AppendChild(scrollContainer,container)
toggleContainer.AppendChild(toggleContainer,toggleIcon)
sidebar.AppendChild(sidebar, toggleContainer)
sidebar.AppendChild(sidebar, scrollContainer)

return details
//return details
return sidebar
}

+ 1
- 1
modules/markup/sanitizer.go View File

@@ -69,7 +69,7 @@ func ReplaceSanitizer() {
sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(ui checkbox)|(ui checked checkbox)|(emoji))$`)).OnElements("span")

// Allow generally safe attributes
generalSafeAttrs := []string{"abbr", "accept", "accept-charset",
generalSafeAttrs := []string{"abbr", "accept", "accept-charset", "class",
"accesskey", "action", "align", "alt",
"aria-describedby", "aria-hidden", "aria-label", "aria-labelledby",
"axis", "border", "cellpadding", "cellspacing", "char",


+ 2
- 0
modules/setting/setting.go View File

@@ -312,9 +312,11 @@ var (
EnableHardLineBreak bool
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
FileExtensions []string
EnableToc bool
}{
EnableHardLineBreak: true,
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
EnableToc: true,
}

// Admin settings


+ 1
- 0
public/img/holder.svg View File

@@ -0,0 +1 @@
<svg height="56" viewBox="0 0 12 56" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m3.86950483 4.06524758 8.13049517-4.06524758v56l-8.13049517-4.0652476c-2.3714882-1.1857441-3.86950483-3.6095859-3.86950483-6.2609903v-35.3475242c0-2.65140439 1.49801663-5.07524622 3.86950483-6.26099032z" fill="#e3e9ed" fill-rule="evenodd" transform="matrix(-1 0 0 1 12 0)"/></svg>

+ 3
- 5
templates/repo/view_file.tmpl View File

@@ -1,4 +1,3 @@

<div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content gallery">
<h4 class="file-header ui top attached header">
<div class="file-header-left">
@@ -80,9 +79,9 @@
{{end}}
</h4>
<div class="ui attached table unstackable segment">
<div class="file-view {{if .IsMarkup}}{{.MarkupType}} markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextFile}}code-view{{end}}">
<div class="file-view {{if .IsMarkup}}{{.MarkupType}} markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextFile}}code-view{{end}}">
{{if .IsMarkup}}
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
{{else if .IsRenderedHTML}}
<pre>{{if .FileContent}}{{.FileContent | Str2html}}{{end}}</pre>
{{else if not .IsTextFile}}
@@ -134,8 +133,6 @@ function submitDeleteForm() {
$("#delete-file-form").submit()
}
}


const baseUrls = {};
const justDomain = /^[^:]+:\/*[^/]*$/;
const protocol = /^([^:]+:)[\s\S]*$/;
@@ -212,4 +209,5 @@ function showNoteBook(){
}
showNoteBook()


</script>

+ 42
- 0
web_src/js/index.js View File

@@ -88,6 +88,48 @@ const commentMDEditors = {};
// Silence fomantic's error logging when tabs are used without a target content element
$.fn.tab.settings.silent = true;

$(document).ready(function(){
if ($('.file-view.markdown').length && $('.file-view.markdown').length>0) {
setTimeout(()=>{
const eleList =document.querySelectorAll('div.anchor-wrap .anchor')
const navList = document.querySelectorAll('.markdown_toc .catalog-li')
const toggleIcon = document.getElementsByClassName('toggle-container')
toggleIcon[0].addEventListener('click',function(){
if(!this.parentElement.classList.contains('hidden')){
this.parentElement.classList.add('hidden')
this.firstChild.classList.replace('ri-arrow-drop-left-line','ri-arrow-drop-right-line')
}else{
this.parentElement.classList.remove('hidden')
this.firstChild.classList.replace('ri-arrow-drop-right-line','ri-arrow-drop-left-line')
}
})
navList[0].classList.add('active')
let timeout = null;
$(window).on('scroll', () => {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(function(){
let flag = false
eleList.forEach((k,index)=>{
if(isInviewPort(k) && !flag){
flag = true
navList.forEach((ele)=>{
ele.classList.remove('active')
})
navList[index].classList.add('active')
}
})
},50)
})
},0)
}
})
function isInviewPort(element){
const viewWidth = window.innerWidth || document.documentElement.clientWidth || ''
const viewHeight = window.innerHeight || document.documentElement.clientHeight || ''
const {top,right,bottom,left} = element.getBoundingClientRect()
return (top>=0 && left>=0 && right<viewWidth && bottom<=viewHeight)
}

function initCommentPreviewTab($form) {
const $tabMenu = $form.find(".tabular.menu");
$tabMenu.find(".item").tab();


+ 94
- 4
web_src/less/_markdown.less View File

@@ -1,5 +1,5 @@
.markdown:not(code) {
overflow: hidden;
// overflow: hidden;
font-size: 16px;
line-height: 1.6 !important;
word-wrap: break-word;
@@ -8,9 +8,9 @@
padding: 3em;
}

&.file-view {
padding: 2em 2em 2em !important;
}
// &.file-view {
// padding: 2em 2em 2em !important;
// }

> *:first-child {
margin-top: 0 !important;
@@ -524,3 +524,93 @@
user-select: none;
}
}


.markdown_catalog{
position: sticky;
top: 1px;
float: left;
}

.markdown_catalog.hidden .scroll-container{
width: 0px !important;
}
.markdown_catalog .scroll-container {
overflow: hidden;
width: 200px;
min-height: calc(50vh);
-webkit-transition: width .3s ease;
transition: width .3s ease;
}
.markdown_catalog .toggle-container{
position: absolute;
width: 12px;
height: 56px;
top: 50%;
left: 100%;
background: url("/img/holder.svg");
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%);
cursor: pointer;
}
.markdown_catalog .toggle-container .icon{
display: inline-block;
margin-top: 19px;
margin-left: -2px;
color: #B7BDC8;
}
.markdown_catalog .scroll-container .container {
padding: 16px;
width: inherit;
max-height: calc(100vh - 1px);
overflow-x: hidden;
overflow-y: auto;
-webkit-transition: margin-left .3s ease;
transition: margin-left .3s ease;
}
.markdown_catalog .container>.markdown_toc {
padding-left: 0;
font-size: 14px;
}
.markdown_catalog .container .markdown_toc {
margin: 0;
padding-left: 16px;
font-size: 13px;
font-weight: 400;
line-height: 28px;
}
.markdown_catalog .container .catalog-li {
list-style: initial;
list-style-position: inside;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.markdown_catalog .container .no-catalog-li{
list-style: none;
}
.markdown_catalog .container .catalog-li.active {
color: #2185d0;;
font-weight: 600;
}
.markdown_catalog .container .catalog-li.active a {
color: #2185d0;;
}
.markdown_catalog .container .catalog-li a {
padding: 6px 0;
outline: none;
color: #40485b;
}
.markdown-content {
min-height: calc(50vh);
border-left: 1px solid #dce3e8;
padding: 1rem 2rem 1rem 2rem;
overflow: auto;
line-height: 1.6;
font-size: 16px;
-webkit-text-size-adjust: 100%;
word-wrap: break-word;
}
html {
scroll-behavior: smooth;
}

Loading…
Cancel
Save