Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TOC for Wiki Pages #822

Closed
2 of 6 tasks
MorphBonehunter opened this issue Feb 2, 2017 · 21 comments · Fixed by #19873
Closed
2 of 6 tasks

TOC for Wiki Pages #822

MorphBonehunter opened this issue Feb 2, 2017 · 21 comments · Fixed by #19873
Labels
issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented topic/wiki type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Milestone

Comments

@MorphBonehunter
Copy link
Contributor

MorphBonehunter commented Feb 2, 2017

  • Gitea version (or commit ref): 1.0.1
  • Git version: 2.11.0
  • Operating system: Arch Linux
  • Database (use [x]):
    • PostgreSQL
    • MySQL
    • SQLite
  • Can you reproduce the bug at https://try.gitea.io:
    • Yes (provide example URL)
    • No
    • Not relevant
  • Log gist:

Description

This is an "clone" of gogs issue 3931
Having an TOC in Wiki Pages could be helpfull.


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@lunny lunny added this to the 1.x.x milestone Feb 2, 2017
@lunny lunny added the type/feature Completely new functionality. Can only be merged if feature freeze is not active. label Feb 2, 2017
@lunny
Copy link
Member

lunny commented May 4, 2017

It seems it's already supported?

@MorphBonehunter
Copy link
Contributor Author

A table of contents is supported in Gittea? I mean an TOC based on headings in the Wiki Pages.
Then i do not know how to use it (could not find anything).
The original GOGS Issue is still open and so i think if this works, it was implemented in this project only.

@lunny
Copy link
Member

lunny commented May 5, 2017

could you see https://try.gitea.io/lunny/vscode/wiki, is the right bar what you want?

@MorphBonehunter
Copy link
Contributor Author

@lunny although this is an cool feature (is this already implemented?) i mean such thing like this: https://i.stack.imgur.com/IgJJd.png

@lunny
Copy link
Member

lunny commented May 5, 2017

@MorphBonehunter yes, https://try.gitea.io/lunny/vscode/wiki is the demo of the master. I see what's you want. The content is generated automatically by a special syntax?

@MorphBonehunter
Copy link
Contributor Author

@lunny yes that's what i thought about. Maybe this isn't possible with an markup renderer atm.
I thought something like this in an README.md:

%%TOC%%

# heading one
# heading two
## subheading

and the %%TOC%% is replaced with:

  1. heading one
  2. heading two
    2.1 subheading

Maybe this could not realized and we are bound to such things like https://github.com/thlorenz/doctoc and have to generate the TOC befor commit.

@zettar
Copy link

zettar commented Nov 6, 2018

TOC is really important. Documentation (written in English) is IMHO just as important as the code, which is "a kind of" of English too. With code, we have all sorts of neat code browsers. With documentation, a TOC is a navigation aid, which helps review the content. The search feature is also critical but that's covered by another issue #3760

@stale
Copy link

stale bot commented Jan 5, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you for your contributions.

@stale stale bot added the issue/stale label Jan 5, 2019
@fangchin
Copy link

fangchin commented Jan 5, 2019

It's a pity that so little attention is paid to one of the most critical factors of a successful software project: the documentation. When multiple pages are authored, TOCs are a critical part of the overall structure. IMHO this issue deserves far more attention than what it has got so far.

@stale stale bot removed the issue/stale label Jan 5, 2019
@lafriks
Copy link
Member

lafriks commented Jan 5, 2019

@fangchin feel free to submit PR for this

@fangchin
Copy link

fangchin commented Jan 6, 2019

@lafriks I will discuss with my team and get a better idea about the scope. If we cannot work on a PR ourselves, I will see if we see other options. The overall progress of this project has been delightful, but IMHO the documentation handling side (wiki, doc search) etc has not been on par with the code handling side.

@stale
Copy link

stale bot commented Mar 7, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you for your contributions.

@stale stale bot added the issue/stale label Mar 7, 2019
@davydov-vyacheslav
Copy link

is there any work on it? Should we keep this item as reviewed ?

@stale stale bot removed the issue/stale label Mar 7, 2019
@mrsdizzie
Copy link
Member

FWIW, it is possible to script a similar feature if interested:

When editing the wiki on the Web interface, gitea clones a temporary copy of it on disk. If you create a custom pre-commit hook and make sure your gitea user has something like this in their ~/.gitconfig:

[init]
    templatedir = /home/git/git-templates

And stick the script in hooks/pre-commit. Then it will be installed when gitea creates a temporary clone of the wiki to edit. Each page save is a commit to the temporary repo, so that script would be run on each save. You can then manipulate any data from the wiki since it is just a bunch of text files which provides almost infinite flexibility for this case.

Heres a small example of one that would update the sidebar with the last 3 changed pages on each page save (added some comments):

#!/usr/bin/perl

use strict;
use warnings;

my $git_url = qx (git remote get-url origin);
chomp $git_url;
if ( index( $git_url, ".wiki" ) == -1 ) {
    # we aren't in a wiki, lets bail
    exit;
}

# wiki is currently structured as a git repo with one level of markdown files
# for each page. _Sidebar.md and _Footer.md are special and should be ignored
my @md_files = glob '"[!_]*.md"';

# When cloned, all files have the same mtime so we need git to show us the order
# in which the have been changed
my @md_files_recent
    = qx (git --no-pager log --oneline --name-only --pretty=format: | grep -iv _Sidebar.md | sed -r '/^\\s*\$/d\');
chomp @md_files_recent;

# The file that triggered this script won't be listed in the log until after this commit is complete
# so grab the name here and stick it at the top of the recently changed list

# FIX ME check if a file was deleted, since that isn't a useful recently edied link
my @currently_changing_files = qx (git diff --cached --name-only | grep -iv _Sidebar.md | sed -r '/^\\s*\$/d\');
chomp @currently_changing_files;

if ( $currently_changing_files[0] ) {
    unshift @md_files_recent, $currently_changing_files[0];
}

# strip .md
for (@md_files_recent) {s{\.[^.]+$}{}g}

# loop through array in order and remove files we've already seen so we can get
# a uniq list of recently changed files while preserving the expected order
@md_files_recent = do {
    my %seen;
    grep { !$seen{$_}++ } @md_files_recent;
};

@md_files = sort { lc($a) cmp lc($b) } @md_files;

my $final_sidebar_content;

$final_sidebar_content .= "### Recently Changed files\n";

if ( $#md_files_recent >= 2 ) {

    foreach my $i ( 0 .. 2 ) {
        my $link_title = $md_files_recent[$i];
        my $link       = $link_title;
        $link_title =~ s/-/ /gis;
        $final_sidebar_content .= "* [$link_title]($link)\n";
    }
}
else {
    foreach my $i ( 0 .. $#md_files_recent ) {
        my $link_title = $md_files_recent[$i];
        my $link       = $link_title;
        $link_title =~ s/-/ /gis;
        $final_sidebar_content .= "* [$link_title]($link)\n";
    }
}

my $filename = '_Sidebar.md';
open( my $fh, '>', $filename ) or die "Could not open file '$filename' $!";
print $fh $final_sidebar_content;
close $fh;
system( "git", "add", "$filename" );

You can easily have it go through all of the files and extract headers as well to create a TOC that you can then inject into any file.

@lunny lunny added the issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented label Mar 17, 2019
@Cherrg
Copy link
Contributor

Cherrg commented May 22, 2019

Hello everybody,

i've modified some JS i've written some time ago to create the toc - if there is a rendered md file. Feel free to use this code.

If there should be a option inside repository settings to enable/disable the script, I couln't create the pull request. (My golang is too bad.)
Otherwise I could create the js file and add a line in html template. Leave a comment if I should open such a merge/pull request. (If this solution is ok.)

Requirements:

Benefints:

  • JS don't alter files inside git repository.
  • fast and small: calculation done on client side, using pure js

Disadvantage:

  • Does not work with disabled JS.

Here is the Code:

(function() {
	// html listings ----------------------------------------------------
	let openedLists, listEopen;
	// close list
	const _closeList = function (count) {
		let out = '';
		if(count == false || count == 0 || count == 'undefined' || typeof(count) == 'undefined' ) {
			count = openedLists.length;
		} else {
			count = Math.min(count, openedLists.length);
		}
		while (count > 0){
			out += '</li>' + openedLists.pop();
			listEopen = true;
			count--;
		}
		return out;
	};
	// open list
	const _openList = function (level) {
		let out = '<ul>';
		openedLists.push('</ul>');
		listEopen = false;
		return out;
	};
	// handle list element
	// create valid html list
	const __list = function (line, level, id) {
		let out = '';
		let diff = level - openedLists.length;
		if(diff > 0) { //open new level
			out += _openList(level);
			out += __list(line,level, id);
		}  else if(diff < 0 ) {
			out += _closeList(-diff);
			out += __list(line, level, id);
		} else { // only add list element
			out += ((listEopen)?'</li>':'') + '<li><a href="#' + id + '" rel="nofollow">' + line + '</a>';
			listEopen = true;
		}
		return out;
	};
	/** 
	 * find headlines and create list ----------------------------------
	 * @param target	Element	 target container where toc should be created
	 */
	const create_toc_inside = function(target) {
		let rm;
		if(target != null) {
			if( (rm = target.querySelector('.auto-toc-wrapper')) != null ) {
				rm.parentNode.removeChild(rm);
			}
			openedLists = []; listEopen = false;
			// get content and create html
			const elms = target.querySelectorAll('h1,h2,h3,h4,h5');
			let html = '';
			for(let i = 0; i < elms.length; i++){
				let l = elms[i].tagName.substr(1); //level
				let t = elms[i].innerText.trim().trim(''); //text
				let id = elms[i].id;
				// create html 
				if(t.length > 0 && l >= 1) {
					html += __list( t, l, id);
				} else {
					html += _closeList(0) + l;
				}
			}
			html += _closeList(0);
			//create elements
			let d = document.createElement('div');
			d.id = 'auto-toc';
			d.className = 'anchor-wrap';
			d.innerHTML = '<h2>Table of Contents</h2>';
			let d2 = document.createElement('div');
			d2.className = 'auto-toc-container';
			d2.innerHTML = html;
			d2.insertBefore(d, d2.firstChild);
			let c = document.createElement('div');
			c.className = 'auto-toc-wrapper';
			c.appendChild(d2);
			//inject toc
			target.insertBefore(c, target.firstChild);
			//set style
			c.style.cssText = "float:right;background:#fff;padding:0 0 7px 20px;position:relative;z-index:1";
			d2.style.cssText = "padding:7px;border:1px solid #333;border-radius:5px";
		}
	};
	// create toc ----------------------------------
	create_toc_inside(document.querySelector('.file-view.markdown')); // md
	create_toc_inside(document.querySelector('.segment.markdown')); // wiki pages
})();

Result - MD files:
image

Result - build in wiki pages (md):
(image is from random gitea wiki page i found with google)
image

Edit: I noticed this issue is for wiki pages, i've only tested on md files (this was what i was looking for in google search).
Edit2: Added line for wiki pages

Cherrg added a commit to Cherrg/gitea that referenced this issue Jun 11, 2019
@terrywh
Copy link

terrywh commented Sep 6, 2019

any news? or plan to merge this feature ? anything ?

@GlitterHorn
Copy link

1.16.5 (and maybe earlier releases, too) changed the classes used for the entities this script targets - "markdown" was changed to "markup". I also wrapped the actual creation of the TOC elements and DOM insertion in an if block that restricts rendering the TOC to articles with more than four headers, so small readmes and the like don't render the TOC.

Updated script is below:

(function() {
        // html listings ----------------------------------------------------
        let openedLists, listEopen;
        // close list
        const _closeList = function (count) {
                let out = '';
                if(count == false || count == 0 || count == 'undefined' || typeof(count) == 'undefined' ) {
                        count = openedLists.length;
                } else {
                        count = Math.min(count, openedLists.length);
                }
                while (count > 0){
                        out += '</li>' + openedLists.pop();
                        listEopen = true;
                        count--;
                }
                return out;
        };
        // open list
        const _openList = function (level) {
                let out = '<ul>';
                openedLists.push('</ul>');
                listEopen = false;
                return out;
        };
        // handle list element
        // create valid html list
        const __list = function (line, level, id) {
                let out = '';
                let diff = level - openedLists.length;
                if(diff > 0) { //open new level
                        out += _openList(level);
                        out += __list(line,level, id);
                }  else if(diff < 0 ) {
                        out += _closeList(-diff);
                        out += __list(line, level, id);
                } else { // only add list element
                        out += ((listEopen)?'</li>':'') + '<li><a href="#' + id + '" rel="nofollow">' + line + '</a>';
                        listEopen = true;
                }
                return out;
        };
        /**
         * find headlines and create list ----------------------------------
         * @param target        Element  target container where toc should be created
         */
        const create_toc_inside = function(target) {
                let rm;
                if(target != null) {
                        if( (rm = target.querySelector('.auto-toc-wrapper')) != null ) {
                                rm.parentNode.removeChild(rm);
                        }
                        openedLists = []; listEopen = false;
                        // get content and create html
                        const elms = target.querySelectorAll('h1,h2,h3,h4,h5');
                        let html = '';

                        if(elms.length > 4) {

                                for(let i = 0; i < elms.length; i++){
                                        let l = elms[i].tagName.substr(1); //level
                                        let t = elms[i].innerText.trim().trim(''); //text
                                        let id = elms[i].id;
                                        // create html
                                        if(t.length > 0 && l >= 1) {
                                                html += __list( t, l, id);
                                        } else {
                                                html += _closeList(0) + l;
                                        }
                                }
                                html += _closeList(0);
                                //create elements
                                let d = document.createElement('div');
                                d.id = 'auto-toc';
                                d.className = 'anchor-wrap';
                                d.innerHTML = '<h2>Table of Contents</h2>';
                                let d2 = document.createElement('div');
                                d2.className = 'auto-toc-container';
                                d2.innerHTML = html;
                                d2.insertBefore(d, d2.firstChild);
                                let c = document.createElement('div');
                                c.className = 'auto-toc-wrapper';
                                c.appendChild(d2);
                                //inject toc
                                target.insertBefore(c, target.firstChild);
                                //set style
                                c.style.cssText = "float:right;background:#fff;padding:0 0 7px 20px;position:relative;z-index:1";
                                d2.style.cssText = "padding:7px;border:1px solid #333;border-radius:5px";
                        }
                }
        };
        // create toc ----------------------------------
        create_toc_inside(document.querySelector('.file-view.markup')); // md
        create_toc_inside(document.querySelector('.segment.markup')); // wiki pages
})();

@yyangdid
Copy link

@GlitterHorn Hello, How to use these codes?

zeripath added a commit to zeripath/gitea that referenced this issue Jun 2, 2022
Automatically add sidebar in the wiki view containing a TOC for the wiki page.

Fix go-gitea#822

Signed-off-by: Andrew Thornton <[email protected]>
@delvh delvh modified the milestones: 1.x.x, 1.17.0 Jun 8, 2022
@GlitterHorn
Copy link

@GlitterHorn Hello, How to use these codes?

Details on customizing Gitea are here. What I did was create a javascript file containing the above script, and saved it to /var/lib/gitea/custom/public/js/toc.js. Then I created a file named footer.tmpl in /var/lib/gitea/custom/templates/custom, which just references the script via HTML:

<script src="/assets/js/toc.js"></script>

Reboot Gitea, and the script should now load with wiki pages, and render the TOC.

@tyroneyeh
Copy link
Contributor

How to disable right TOC menu?

@guidomz
Copy link

guidomz commented Jan 17, 2023

How to disable right TOC menu?

+1 to this. Users/Clients may benefit from having the option to disable the table of contents.

@go-gitea go-gitea locked and limited conversation to collaborators May 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
issue/confirmed Issue has been reviewed and confirmed to be present or accepted to be implemented topic/wiki type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Projects
None yet