20

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

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
Yaroslav Bulatov
  • 7,793
  • 1
  • 19
  • 44
  • 1
    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. – ktm Mar 09 '17 at 01:55
  • maybe it is usful http://library.wolfram.com/infocenter/Conferences/5782/ – tanghe2014 Mar 09 '17 at 04:07

3 Answers3

15

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.

b3m2a1
  • 46,870
  • 3
  • 92
  • 239
  • I created many old-style sub-chapters in my notebooks. On these the code sadly doesn't work. – nilo de roock Mar 12 '21 at 19:01
  • @niloderoock try changing out {"Title", "Chapter", "Section", "Subsection", "Subsubsection"} for the cell styles you actually have – b3m2a1 Mar 12 '21 at 19:03
  • 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. – nilo de roock Mar 12 '21 at 19:15
10

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

matheorem
  • 17,132
  • 8
  • 45
  • 115
2

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…

  • Are you recommending that the OP not use either of the answers already posted? – bbgodfrey Dec 20 '22 at 03:38
  • 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.

    – Maynard Handley Dec 21 '22 at 04:19