20
$\begingroup$

Is there a way to get table of contents I can click on?

I'm looking for something like Table of Contents module in Jupyter notebooks where I can jump between sections easily, here's what my scratch-space usually looks like

enter image description here

$\endgroup$
2
  • 1
    $\begingroup$ You could do something pretty creative with a palette, or the AuthorTools` package has some stuff that might be able to fit your needs. Both of these would require some work though. $\endgroup$
    – ktm
    Mar 9, 2017 at 1:55
  • $\begingroup$ maybe it is usful library.wolfram.com/infocenter/Conferences/5782 $\endgroup$
    – tanghe2014
    Mar 9, 2017 at 4:07

3 Answers 3

15
$\begingroup$

AFAIK there's nothing built-in.

On the other hand I use this sort-of functionality all the time. Here's a quick way to generate a table of contents palette:

nbTOC[nb_] :=
  Button[
      Mouseover[#, Style[#, "HyperlinkActive"]] &@First@NotebookRead@#,
      SelectionMove[#, All, CellContents],
      Appearance -> None,
      BaseStyle -> "Hyperlink",
      Alignment -> Left,
      ImageSize -> 100
      ] & /@

    Cells[CellStyle -> {"Title", "Chapter", "Section", "Subsection", 
       "Subsubsection"}] // CreatePalette[
     Panel[
      Column[#,
       Dividers -> Center],
      ImageSize -> 100
      ],
     FrontEnd`ClosingSaveDialog -> False,
     WindowTitle -> "Table of Contents",
     Magnification -> 1.5
     ] &;
nbTOC[Optional[Automatic, Automatic]] :=
 nbTOC@EvaluationNotebook[]

It assumes you just want to navigate to the "Title", "Chapter", "Section",etc. cells and that the CellObject available at the time the palette was generated will still exist.

The other way I've done this is, at click time, searched for a cell with the same contents and style as the original cell in the notebook. That way it'll work even after the notebook has been closed and reopened.

$\endgroup$
3
  • $\begingroup$ I created many old-style sub-chapters in my notebooks. On these the code sadly doesn't work. $\endgroup$ Mar 12, 2021 at 19:01
  • $\begingroup$ @niloderoock try changing out {"Title", "Chapter", "Section", "Subsection", "Subsubsection"} for the cell styles you actually have $\endgroup$
    – b3m2a1
    Mar 12, 2021 at 19:03
  • $\begingroup$ Do you remember the old sub-chapter style with the red square in front of it? Well, that doesn't exist anymore since v12 but it can be created (hacked) from the chapter style. A disadvantage of this method is that the Style can no longer be queried. - Thanks for your prompt answer btw. $\endgroup$ Mar 12, 2021 at 19:15
10
$\begingroup$

I made a nbTOC based on b3m2a1's nbTOC to make it more ready to use. Improvements are listed below:

  1. Hierarchical TOC levels with different indent, fontsize, boldness and color
  2. width control of the TOC panel
  3. add a scrollbar to support arbitrary long TOC
  4. automatically open inner cell after clicking buttons
  5. an update button at the top of TOC. By clicking it, current TOC window will close and a new one immediately opened.

Here is the function:

ClearAll[openSelectedCellGroup];
openSelectedCellGroup[nb_NotebookObject]:=Module[{},
If[CurrentValue[First@Cells[NotebookSelection[nb]],"CellGroupOpen"]===Closed,
FrontEndTokenExecute[nb,"OpenCloseGroup"]]];

ClearAll[nbTOC];
Options[nbTOC]={
"width"->250,
"indentUnit"->10,
"fontSizeList"->{16,14,12,12,10,10},
"colorList"->{Darker@Red,Darker@Blue,Darker@Darker@Green,Darker@Orange,Black,Darker@Magenta},
"fontWeightList"->{Bold,Bold,Bold,Bold,Bold,Bold}};

nbTOC[notebookObj_NotebookObject,opts:OptionsPattern[]]:=Module[
{defaultCellStyleList,cellList,cellInfoList,currentCellStyleList,
width,indentUnit,fontSizeList,colorList,fontWeightList,
cellStyleAssocFormatStyle,cellStyleAssocButtonImageMargin,buttonList,
cellName,buttonImageMargin,tocNotebookObj,createNewTOCwindows},

width=OptionValue["width"];
indentUnit=OptionValue["indentUnit"];
fontSizeList=OptionValue["fontSizeList"];
colorList=OptionValue["colorList"];
fontWeightList=OptionValue["fontWeightList"];

defaultCellStyleList={"Title","Subtitle","Chapter","Section","Subsection","Subsubsection"};
cellList=Cells[notebookObj,CellStyle->defaultCellStyleList];
cellInfoList=List@@@(NotebookRead/@cellList)[[;;,1;;2]];
currentCellStyleList=DeleteDuplicates@cellInfoList[[;;,-1]];
currentCellStyleList=Cases[defaultCellStyleList,Alternatives@@currentCellStyleList];

cellStyleAssocFormatStyle=Association@Table[
currentCellStyleList[[i]]->With[{i=i},Style[#,fontSizeList[[i]],fontWeightList[[i]],colorList[[i]]]&],
{i,1,Length@currentCellStyleList}];
cellStyleAssocButtonImageMargin=Association@Table[
currentCellStyleList[[i]]->indentUnit*(i-1),
{i,1,Length@currentCellStyleList}];

buttonList=Table[
cellName=Row[{"",(Last[cellInfoList[[i]]]/.cellStyleAssocFormatStyle)[First@cellInfoList[[i]]]}];
(*For unknown reason, Row[{"",xxx}] will make TOC more compact*)
buttonImageMargin=Last[cellInfoList[[i]]]/.cellStyleAssocButtonImageMargin;

With[{cell=cellList[[i]]},
Button[
cellName,
Do[
SelectionMove[cell,All,CellGroup,k];
openSelectedCellGroup[notebookObj]
,{k,1,6}];(*There are at most 6 level of TOC. Open them all from bottom to top*)
SelectionMove[cell,All,CellGroup];
If[Length@Cells[NotebookSelection[notebookObj],CellStyle->{"Title","Subtitle","Chapter","Section","Subsection","Subsubsection"}]==1,
(*If there are no more sections below, then open all sub cell groups, this will open all Out cells*)
FrontEndTokenExecute["SelectionOpenAllGroups"],
(*If there are more sections below, then only open one level further*)
openSelectedCellGroup[notebookObj]];
(*moving back to cell title*)
SelectionMove[cell,All,CellContents]
,
Appearance->None,BaseStyle->"Hyperlink",Alignment->Left,
FrameMargins->{{buttonImageMargin, 0}, {0, 0}},
ImageSize->width]],
{i,1,Length@cellList}];

createNewTOCwindows[]:=Module[{},NotebookClose[tocNotebookObj];nbTOC[notebookObj]];
PrependTo[buttonList,
Button[Style["Click to update",White,Bold,15],createNewTOCwindows[],Background->Black]];

tocNotebookObj=CreatePalette[Column[buttonList,Dividers->Center],FrontEnd`ClosingSaveDialog->False,
WindowMargins->{{Automatic,-5},{Automatic,0}},WindowElements->{"VerticalScrollBar"},WindowTitle->"TOC",Magnification->0.85];
(*SetOptions[EvaluationNotebook[],WindowSize\[Rule]{1047,Automatic},WindowMargins\[Rule]{{0,0},{0,0}}]*)];

nbTOC[opts:OptionsPattern[]]:=nbTOC[EvaluationNotebook[],opts];

enter image description here

Note that I deliberately make non-fixed font style for each level. So if the notebook only contains Section, Subsection, Subsubsection. We will have red biggest font for Section and so on like this

enter image description here

$\endgroup$
2
$\begingroup$

You can create tables of contents programmatically BUT there are a few large problems:

  • the tables are only somewhat “live”. In particular when you change the type of a subsection (eg change from cmd-3 to cmd-4 formatting) it’s hit or miss whether the live ToC will update to show this.

  • more serious is that constructing the ToC becomes something that you need to mark as an initialization cell, and so every time you open your document there’s some friction because you have to force initialization cells to execute before you can do what you want to do. I have been surprised at how much friction this induces.

  • the most obvious ways to construct a ToC (ie the sort of code you will find on the internet) mean that the ToC is a shared construct (single name) when you duplicate a document. This can become very messy if you want to have two versions of the document open simultaneously, eg to compare the current version with an older version.

More generally, I don’t know how Wolfram and the Mathematica employees do it! While writing in MMA is very productive in many ways (I much prefer it to, eg, LyX) it’s just HORRIBLE for large documents. Lack of live ToC is one problem, diff’s between versions is another problem, no version control (eg no easily managed connection to git, like XCode has) is a third problem. Yes, you can (and probably should) write in MMA compared to the alternatives, but the fact that it’s better than everything else, doesn’t mean it’s great; it means that everything else is even worse…

$\endgroup$
2
  • $\begingroup$ Are you recommending that the OP not use either of the answers already posted? $\endgroup$
    – bbgodfrey
    Dec 20, 2022 at 3:38
  • $\begingroup$ Not exactly. I'm pointing out that these solutions, which look good, have some downsides. I'm familiar with this having used these solutions in my own writing of some multi-hundred page documents. The bottom line is that there simply aren't any solutions that I consider very good right now. LyX is far superior to Mathematica in terms of its overview functionality, allowing you to see the entire document and move sections around. But it's clumsier than MMA (IMHO) in terms of the actual writing. For me, the writing is more important than the overview which forces the choice. $\endgroup$ Dec 21, 2022 at 4:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.