{"id":14989235,"url":"https://github.com/moble/jupyter_boilerplate","last_synced_at":"2025-04-12T00:31:45.935Z","repository":{"id":28392994,"uuid":"31907248","full_name":"moble/jupyter_boilerplate","owner":"moble","description":"Adds a customizable menu item to Jupyter (IPython) notebooks to insert boilerplate snippets of code","archived":false,"fork":false,"pushed_at":"2023-02-13T16:31:36.000Z","size":4217,"stargazers_count":71,"open_issues_count":10,"forks_count":15,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-06T00:15:38.928Z","etag":null,"topics":["ipython","javascript","jupyter-notebook","matplotlib","numpy","scipy","snippets","sympy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moble.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-03-09T15:45:02.000Z","updated_at":"2024-12-19T07:11:57.000Z","dependencies_parsed_at":"2024-11-07T09:02:13.626Z","dependency_job_id":"4423346e-b6b8-42ae-b65d-ac042044047f","html_url":"https://github.com/moble/jupyter_boilerplate","commit_stats":{"total_commits":103,"total_committers":5,"mean_commits":20.6,"dds":"0.23300970873786409","last_synced_commit":"31c9c1f723b75b3f0baeea82b65408421107a414"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moble%2Fjupyter_boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moble%2Fjupyter_boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moble%2Fjupyter_boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moble%2Fjupyter_boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moble","download_url":"https://codeload.github.com/moble/jupyter_boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501318,"owners_count":21114651,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ipython","javascript","jupyter-notebook","matplotlib","numpy","scipy","snippets","sympy"],"created_at":"2024-09-24T14:17:54.722Z","updated_at":"2025-04-12T00:31:45.409Z","avatar_url":"https://github.com/moble.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Jupyter notebook snippets menu\n==============================\n\nAdds a customizable menu item to Jupyter notebooks to insert\nsnippets, boilerplate, and examples of code.\n\n![Open snippets menu](screenshot1.png)\n\nThis notebook extension adds a menu item (or multiple menu items, if\ndesired) after the `Help` menu in Jupyter notebooks.  This new menu\ncontains little snippets of code that we all forget from time to time\nbut don't want to google, or are just too lazy to type, or simply\ndidn't know about.  It can also be helpful for people just starting\nout with a programming language, who need some ideas for what to do\nnext — like importing a module, defining variables, or calling\nfunctions.\n\nThe new menu comes with a default value relevant for python\nprogramming — especially scientific computing — though this is fully\nuser-configurable as detailed below.  The default menu is named\n`Snippets`, and contains sub-menus with snippets for a few popular\npython packages, as well as python itself, and some notebook markdown.\n(Note that some of the menus are so large that it is necessary to move\nthe first-level menus to the left so that lower-level menus will fit\non the screen.  This behavior is also user-configurable, as discussed\nin detail [below](#change-direction-of-sub-menus).)\n\nSo, for example, if you are editing a code cell and want to import\nmatplotlib for use in the notebook, you can just click the `Snippets`\nmenu, then mouse over \"Matplotlib\".  This will open up a new sub-menu,\nwith an item \"Setup for notebook\".  Clicking on that item will insert\nthe code snippet at the point where your cursor was just before you\nclicked on the menu.  In particular, for this `matplotlib` example,\nthe following code gets inserted:\n\n```python\nimport numpy as np\nimport matplotlib as mpl\nimport matplotlib.pyplot as plt\n%matplotlib inline\n```\n\nThe inserted text will be selected, so that you can delete it by\npressing backspace or delete, or you can just select another snippet\nto replace it -- and just to highlight what was inserted.\n\nNote that many of the snippets involve variable names prefixed with\n`bp_`.  For example, a new numpy array is created as `bp_new_array`.\nThese are intentionally dumb names that you really should replace.\nFailing to do so could lead to ugly bugs in your code if you use\nmultiple boilerplate snippets with clashing variable names.\n\nSimilarly, some strings are intended to be replaced, such as the axis\nlabels in plots.  These are there to show you what can be done, and to\nremind you to put informative labels in your plots.  If you don't\nwant, e.g., a title on your plot, just remove that line.\n\n\nInstallation\n------------\nTo install this extension alone (without the [main collection of nbextensions](https://github.com/ipython-contrib/jupyter_contrib_nbextensions)), run the following from the command line:\n\n```bash\ngit clone https://github.com/moble/jupyter_boilerplate\njupyter nbextension install jupyter_boilerplate\njupyter nbextension enable jupyter_boilerplate/main\n```\n\nYou can then disable the extension if you want with\n```bash\njupyter nbextension disable jupyter_boilerplate/main\n```\n\n\nBasic menu customization\n------------------------\n\nThe default menu might have too many irrelevant items for you, or may\nnot have something you would find useful.  You can easily customize it\nin the\n[jupyter_nbextensions_configurator](https://github.com/Jupyter-contrib/jupyter_nbextensions_configurator#usage),\nwhich you almost certainly have if you installed this extension the\nnormal way,\nthrough\n[jupyter_contrib_nbextensions](https://github.com/ipython-contrib/jupyter_contrib_nbextensions).\nUsually, you can get to the configurator by pointing your browser to\nhttp://127.0.0.1:8888/nbextensions, though you may have to modify the\nURL if you use a more complicated jupyter server.\n\nOn the configurator page, you will see a number of options (as well as\nthis README) that should be fairly self-explanatory, allowing you to\nremove any of the default menu items, or add a custom menu within the\n\"Snippets\" menu.  The custom menu is written in JSON, and a simple\n(and useless) example is given that should be easy to modify as\nneeded.\n\n\n\nAdvanced menu customization\n---------------------------\n\nIt is also possible to extensively customize the menus in far more\ncomplex ways using your `custom.js` file.  For example, you can change\nthe order of menu items, add more custom sub-menus under the\n\"Snippets\" menu, and custom menus alongside \"Snippets\" in the menu\nbar, or even add menus in other places, like inside the \"Insert\" menu.\n\nYou can find the path to `custom.js` by running the command\n\n```bash\necho $(jupyter --config-dir)/custom/custom.js\n```\n\nFor Mac and linux users, the result is probably\n`~/.jupyter/custom/custom.js`.  If this file or the directory\ncontaining it do not exist, you can simply create them.\n\nThe customization process is best explained through examples, which\nare available in the `examples_for_custom.js` file in this directory.\nNote that there's a lot of explanation here, but it's all actually\npretty simple.  Give it a try, and you'll pick it up quickly.  Note\nthat using this method can make it so that options selected in the\nconfigurator are ignored.\n\nThe theory behind this customization is that the menu is represented\nby a nested JavaScript array (which is just like a python list).  So\nto change the menu, you just need to change that array.  And each menu\n*item* inside this array is represented by\na [JavaScript \"object\"](http://api.jquery.com/Types/#Object) (which is\njust like a python dictionary).  So to change a menu item, you just\nhave to change that object.\n\nAgain, this makes more sense when looking at example, as follows.\n\n\n### Add a custom sub-menu with simple snippets\n\nSuppose you want to make a new sub-menu with your favorite snippets at\nthe bottom of `Snippets`.  You create a new object for the menu item,\nand then just \"push\" it onto the default menu.  Do this by inserting\nsome lines into your `custom.js`, so that it looks like this:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    var horizontal_line = '---';\n    var my_favorites = {\n        'name' : 'My favorites',\n        'sub-menu' : [\n            {\n                'name' : 'Menu item text',\n                'snippet' : ['new_command(3.14)',],\n            },\n            {\n                'name' : 'Another menu item',\n                'snippet' : ['another_new_command(2.78)',],\n            },\n        ],\n    };\n    snippets_menu.options['menus'] = snippets_menu.default_menus;\n    snippets_menu.options['menus'][0]['sub-menu'].push(horizontal_line);\n    snippets_menu.options['menus'][0]['sub-menu'].push(my_favorites);\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\nNow, if you refresh your notebook, you'll see a new menu item named \"My\nfavorites\".  Hover over it, and it will pop up a sub-menu with two more\noptions.  Click the first one, and it will insert `new_command(3.14)` into\nyour notebook wherever the cursor was.\n\nWe discuss how all this works below.  But first, we need to slightly generalize\nthe example above to work with more complicated snippets.\n\n\n### More complicated snippets\n\nThe example above inserted simple one-line snippets of code.  Those snippets\ndidn't have any quotation marks (single or double), backslashes, or newlines,\nwhich made everything easy.  Unfortunately, JavaScript doesn't deal too well\nwith strings.  (There are no raw triple-quoted strings, like in python.)  So\nthere are just three things to remember when writing snippets.\n\n  1. Quotation marks can be a tiny bit tricky.  There are a few options:\n    1. The obvious option is to enclose your snippets in single quotation marks\n       (`'`), and use only double quotation marks (`\"`) within the snippet\n       itself.\n    2. Just as easy is to enclose your snippets in double quotation marks\n       (`\"`), and use only single quotation marks (`'`) within the snippet\n       itself.\n    3. You can also escape single quotation marks inside single quotation marks\n       as `\\'`.\n\n  2. Newlines are even trickier, but the extension takes care of this for you\n     as long as you put separate lines of code as separate elements of the\n     `snippet` array.  Generally, there's no reason to put a literal newline in\n     your snippets.\n\n  3. JavaScript will treat backslashes as if they're trying to escape whatever\n     comes after them.  So if you want one backslash in your output code,\n     you'll need to put two backslashes in.\n\nThis is all best described with another example.  Let's change the first\nfunction above, to give it some more lines and some quotes:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    var horizontal_line = '---';\n    var my_favorites = {\n        'name' : 'My $\\\\nu$ favorites',\n        'sub-menu' : [\n            {\n                'name' : 'Multi-line snippet',\n                'snippet' : ['new_command(3.14)',\n                             'other_new_code_on_new_line(\"with a string!\")',\n                             'stringy(\\'escape single quotes once\\')',\n                             \"stringy2('or use single quotes inside of double quotes')\",\n                             'backslashy(\"This \\\\ appears as just one backslash in the output\")',\n                             'backslashy2(\"Here are \\\\\\\\ two backslashes\")',],\n            },\n            {\n                'name' : 'TeX appears correctly $\\\\alpha_W e\\\\int_0 \\\\mu \\\\epsilon$',\n                'snippet' : ['another_new_command(2.78)',],\n            },\n        ],\n    };\n    snippets_menu.options['menus'].push(snippets_menu.default_menus[0]);\n    snippets_menu.options['menus'][0]['sub-menu'].push(horizontal_line);\n    snippets_menu.options['menus'][0]['sub-menu'].push(my_favorites);\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\nNote the code output by the first item contains all sorts of interesting\nstrings.  Also, the menu title of the second item contains TeX, which will\ndisplay correctly, and is used in some of the default menus to show the\nstandard symbols for physical constants.  For more examples, look at the\ndefault menus stored in the `snippets_menu` directory -- mostly under `python`.\n\n\n### How it works: Creating new menu items\n\nEach of the menu items above is a JavaScript object (like a python `dict`),\nwith some attributes -- `name` and `sub-menu` for the main menu item, and\n`name` and `snippet` for the sub-menu items.  In general, any menu object can\nhave any of the following properties:\n\n  1. `name`: Text that appears in the menu.  Note that this can include latex,\n     as the menus are processed by MathJax after being loaded.\n  2. `sub-menu`: An array of more menu items\n  3. `snippet`: An array of strings turned into code when the menu item is\n     clicked\n  4. `internal-link`: Link to some place on the present page.  For example,\n     this could be `#References`, to link to the `References` section of any\n     notebook you're in.\n  5. `external-link`: This just a link to some external web page, which will be\n     identified with a little icon, just like in the standard notebook \"Help\"\n     menu.  When clicked, the link will open in a new window/tab.\n  6. `menu-direction`: If the value of this property is `left`, this menu's\n     sub-menus open on the left.  This is useful when the top-level menu is\n     inserted as an item within other menu items.  See\n     [below](#change-direction-of-sub-menus) for examples.\n  7. `sub-menu-direction`: If the value of this property is `left`, sub-menus\n     within this menu's sub-menus open on the left.  This is used by default\n     for items under the `Snippets` menu to help ensure that nested menus\n     don't become too large to fit on the screen.  See\n     [below](#change-direction-of-sub-menus) for examples.\n\nThe `name` property is the only required one, though you'll probably want at\nleast one other property.  The `sub-menu` contains menu objects that again may\nhave any of these properties, so you can easily nest menus.  You can also\ncombine a `snippet` with a `sub-menu`, so that there's a default value as well\nas a sub-menu.  However, the last three are mutually exclusive: `snippet` will\noverride any `-link`; an `internal-link` will override an `external-link`.\n\n\n### How it works: Splicing new menu items into the old\n\nBesides just creating the menu items, we may want to join together previously\ncreated items.  That's the purpose of this line in the code above:\n\n```javascript\n    snippets_menu.options['menus'][0]['sub-menu'].push(my_favorites);\n```\n\nThis uses\nthe [JavaScript `push`](http://www.w3schools.com/jsref/jsref_push.asp)\nfunction to insert the new menu `my_favorites` menu into the last slot\nof `snippets_menu.options['menus'][0]['sub-menu']`, which is the set\nof menus under the heading `Snippets`.\n\nIf you think about this last point, you'll realize that `Snippets` is\njust the `0` slot of an array of menus.  If you want a new menu right\nin the menu bar, you could add `my_favorites` right to that top-level\narray, with something like this:\n\n```javascript\n    snippets_menu.options['menus'].push(snippets_menu.default_menus[0]);\n    snippets_menu.options['menus'].push(my_favorites);\n```\n\nThis would place your favorites after the default `Snippets` menu; to\nput it before, just swap the order in which you `push`:\n\n```javascript\n    snippets_menu.options['menus'].push(my_favorites);\n    snippets_menu.options['menus'].push(snippets_menu.default_menus[0]);\n```\n\n(In general, to add a new element at a given index of an array, you\ncould also just use\nthe [splice](http://www.w3schools.com/jsref/jsref_splice.asp)\nfunction.)\n\nThis might be useful if you have one set of very frequently used\ncommands, and want immediate access, without going through various\nlevels of the usual menu.  A useful example of this is\nshown [below](#starting-over-with-the-menus).  The `splice` function\ncan also be used to delete items from the array, as\ndescribed [next](#deleting-menu-items).\n\n\n### Other menu manipulations\n\nTo rearrange menu items, just use standard JavaScript techniques.  The two most\nlikely examples are deleting and rearranging menu items, but we'll also see\nthat other manipulations are easy.  We can also change where the new menus go,\nand what they look like.\n\n#### Deleting menu items\n\nTo delete an item, just `splice` nothing into it.  Let's suppose, for example,\nthat you want to remove the option to set up matplotlib for a script, which is\nthe `1` item of the \"Matplotlib\" menu:\n\n```javascript\nsnippets_menu.python.matplotlib['sub-menu']\n```\n\nRemember that `[1]` is the second element of \"Matplotlib\"'s sub-menu\nlist.  So the following code will do the trick\n\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    snippets_menu.python.matplotlib['sub-menu'].splice(1, 1); // Delete 1 element starting at position 1 of the sub-menu\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\nThe first `1` in the argument to `splice` says to work on the element at\nposition 1; the second `1` says to delete 1 element of the array.\n\n\n#### Rearranging menu items\n\nFollowing the example above, suppose you don't want to delete the second setup\nitem under \"Matplotlib\", but instead want to swap those first two items.  To\nmake this swap, you need to do the usual trick of storing one element in a\ntemporary variable, and then reassign appropriately.  The following code\nachieves this purpose:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    var tmp = snippets_menu.python.matplotlib['sub-menu'][0];\n    snippets_menu.python.matplotlib['sub-menu'][0] = snippets_menu.python.matplotlib['sub-menu'][1];\n    snippets_menu.python.matplotlib['sub-menu'][1] = tmp;\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\n#### Change direction of sub-menus\n\nEach sub-menu may be placed to the right or left side of the menu item\ncontaining it.  This is controlled by the `menu-direction` and\n`sub-menu-direction` properties of the container.  By default, both are set to\n`right` for all menus, but `sub-menu-direction` is set to `left` for the\ndefault `Snippets` menu, which means that all of its sub-menus open to the\nleft side.  This is important because the menus may be nested quite deeply, and\nneed to fit on the screen.  For example, the SciPy CODATA constants and SymPy's\northogonal functions will easily extend far past the right-hand edge of the\nnotebook without this feature.  That means the window size would abruptly\nincrease when you mouse over these menus, and would abruptly collapse when you\nmouse out of them.  So by opening them to the left, we gain enough space to\nkeep everything on the screen.\n\nBut these are configurable properties.  If, for example, you want to change the\ndefault menus to open on the right (which would be the more standard behavior),\nyou can use this:\n\n```javascript\n    snippets_menu.default_menus[0]['sub-menu-direction'] = 'right';\n```\n\nThis may be particularly useful if we change the position of the menus, as in\nthe next examples.\n\n\n#### Starting over with the menus\n\nEach of the menu items under the default `Snippets` menu is\nindividually available as part of the `snippets_menu` object defined\nin our JavaScript examples.  So if you want, you could just use them\nto build your own version of the menu.  For example, suppose use SymPy\nand Numpy most frequently, so you want easy access to their menus,\nwithout having to click `Snippets` first.  And then suppose you still\nwant most of the other `Snippets` items, but less frequently so they\ncan stay in their menu, except that you really never use pandas.  You\ncan create your own menu as follows:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    snippets_menu.default_menus[0]['sub-menu'].splice(3, 2); // Remove SymPy and pandas\n    snippets_menu.python.sympy['sub-menu-direction'] = 'left'; // Point new SymPy menus to left\n    snippets_menu.python.numpy['sub-menu-direction'] = 'left'; // Point new Numpy menus to left\n    snippets_menu.options['menus'].push(snippets_menu.default_menus[0]); // Start with the remaining \"Snippets\" menu\n    snippets_menu.options['menus'].push(snippets_menu.python.sympy); // Follow that with a new SymPy menu\n    snippets_menu.options['menus'].push(snippets_menu.python.numpy); // Follow that with a new Numpy menu\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\nThe default menu group is `snippets_menu.default_menus`, and the SymPy sub-menu\nis `snippets_menu.python.sympy`.  You can see that we've manipulated them above\nby removing two elements from the default menu, using the `splice` command.\nWe've also added a new property to the SymPy menu to make its sub-menus open to\nthe left, instead of the right -- which is necessary to keep all of SymPy's\nextensively nested menus on the screen.  (In fact, the list of orthogonal\npolynomials under \"Special functions\" in the SymPy menu are some of the widest\nmenus in the default set.)  Finally, we've combined the modified default menu\nwith the modified SymPy menu into one new list.\n\nThis gives us the original `Snippets` menu with SymPy and pandas removed, as\nwell as new menus devoted to just SymPy and Numpy right in the menu bar:\n\n![Opened snippets menu after adjustments](screenshot2.png)\n\nYou can see that the two items are indeed removed from `Snippets`, and\n\"SymPy\" and \"Numpy\" now have places of honor right in the menu bar.\nYou can, of course, swap their order in the code above, or make any\nnumber of further alterations.\n\n\n#### Changing the insertion point\n\nYou might want to change the order of the menus in the navbar (that\ntop-level bar with \"File\", etc.).  For example, it might feel\nparticularly natural to have \"Help\" as the last item, so maybe you'd\nprefer to put the `Snippets` menu *before* the \"Help\" menu.  Or you\nmay prefer to maintain the structure of the menus in the navbar, and\nwould rather have the `Snippets` menu *inside* of some other top-level\nmenu -- like the \"Insert\" menu.  Personally, I prefer to have the\n`Snippets` menu in its default position for easy access.  But it's\ncertainly possible to put it other places.\n\nTo help do this, there are two additional options available, which can\nbe set either in the configurator or in `custom.js`.  Their default\nvalues give us the usual placement of the `Snippets` menu; by giving\nthem different values, we can change the placement.  These options are\n\n  1. `sibling`: This is an HTML node next to our new menu,\n     presumably\n     [selected with `jQuery`](http://learn.jquery.com/using-jquery-core/selecting-elements/).\n     The default value is `$(\"#help_menu\").parent()`, which is the\n     \"Help\" menu.  For the configurator, `.parent()` is automatically\n     appended.\n  2. `insert_before_sibling`: This is just a string controlling where\n     the new menu will be inserted relative to its sibling. The\n     default value is `false`.  If you change it to `true`, the new\n     menu will be inserted before the sibling.\n\nSo placing the `Snippets` menu *before* the \"Help\" menu is as easy as\nchecking the box for that second option.  For more complicated uses,\nyou may need to set these values from `custom.js`.  For example, f you\nwant to put the new `Snippets` menu as an item inside the standard\n\"Insert\" menu, and include an additional horizontal divider, you can\nuse this:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    snippets_menu.default_menus[0]['menu-direction'] = 'left'; // Open top-level menu to the left...\n    snippets_menu.default_menus[0]['sub-menu-direction'] = 'right'; // ...and sub-menus to the right.\n    snippets_menu.options['menus'].push('---', snippets_menu.default_menus[0]); // Add horizontal line and default menus\n    snippets_menu.options['sibling'] = $(\"#insert_cell_below\"); // Find the place at which to insert the new menus\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\nHere's what that looks like:\n\n![Opened snippets menu under \"Insert\" menu](screenshot3.png)\n\nAnd of course, you can combine this selection of the insertion point with other\ntechniques above, where you change the content of the menus.\n\n\n#### Multiple menus in separate locations\n\nFinally, we have one more interesting example that brings together various\nthreads from the previous examples.  It is possible to place multiple menus in\ndifferent locations.  For example, suppose we want to combine two of the examples\nabove, where [(1)](#starting-over-with-the-menus) we separated \"SymPy\" into its\nown menu on the navbar, and [(2)](#changing-the-insertion-point) we placed the\n`Snippets` menu inside the \"Insert\" menu.  That is, you might want \"SymPy\"\nto be conveniently placed, but you want the rest of the `Snippets` to stay\nunder the \"Insert\" menu.\n\nTo add these two separate menus, we place the first with the usual\napproach, and then place the second with another function,\n`snippets_menu.menu_setup`.  The former is mostly just a wrapper to\nthe latter, except that it also inserts JavaScript and CSS elements\ninto the notebook.  Note that `menu_setup` does not have any default\nvalues; you must always pass the `sibling` and `insert_before_sibling`\narguments.\n\nSo, putting it all together, the code needed for this arrangement is as\nfollows:\n\n```javascript\nrequire([\"nbextensions/snippets_menu/main\"], function (snippets_menu) {\n    console.log('Loading `snippets_menu` customizations from `custom.js`');\n    var sympy_menu = [snippets_menu.python.sympy,];\n    sympy_menu[0]['sub-menu-direction'] = 'left';\n    snippets_menu.options['menus'] = sympy_menu;\n    snippets_menu.default_menus[0]['sub-menu'].splice(3, 1); // Remove SymPy from defaults\n    snippets_menu.default_menus[0]['menu-direction'] = 'left';\n    snippets_menu.default_menus[0]['sub-menu-direction'] = 'right';\n    var sibling = $(\"#insert_cell_below\");\n    var inserted_menu = [\n        '---',\n        snippets_menu.default_menus[0],\n    ];\n    snippets_menu.menu_setup(inserted_menu, sibling, 'after');\n    console.log('Loaded `snippets_menu` customizations from `custom.js`');\n});\n```\n\n\nTroubleshooting\n---------------\n\nThe first step is to make sure that the default setup can be loaded.\nComment out whatever you've got in `custom.js`, and add in the simple\nconfiguration from [the beginning](#installation).  If that doesn't\nwork, try the following steps\nsuggested\n[here](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/wiki#troubleshooting):\n\n  1. Clear your browser cache or start a private browser tab.\n  2. Verify your `custom.js` is the one the notebook is seeing, by opening it\n     in the browser: \u003chttp://127.0.0.1:8888/static/custom/custom.js\u003e (as\n     opposed to looking at the file directly outside of your browser, which may\n     not be the `custom.js` loaded if you are using a `virtualenv`).\n  3. Verify the extension can be loaded by the Jupyter notebook, for example:\n     \u003chttp://127.0.0.1:8888/nbextensions/snippets_menu/main.js\u003e.  You\n     should see a page with lots of JavaScript code, and should *not*\n     see a 404 error.\n  4. Check for error messages in the JavaScript console.\n\nNow, assuming the basic installation works, it must be something wrong\nin your customization.  (Or maybe a new bug you've uncovered...)\n\nSometimes, the menu(s) might simply not appear.  This is most likely\ndue to a syntax error in your menu.  You can find out in Chrome by\ngoing to \"View\" -\u003e \"Developer\" -\u003e \"JavaScript console\".  You'll see a\nbunch of output.  Red lines are usually errors (some of which are\nprobably *not* due to your menu error).  On the right side of those\nlines, you'll see the file where the error came from, and possibly\neven the line number that's causing the trouble.  Find an error that\nlinks to either `snippets_menu/main.js` or `custom.js`, and click on\nit.  Then try to figure out what went wrong.  The most common error\nI've encountered is \"Unexpected string\", which might indicate a\nmissing comma, or an improperly escaped quote.  Note that sometimes\nthe error will point to the first thing *after* the real problem.\n\nOr maybe the menu did appear, but it doesn't work properly.  You can\nalso inspect the actual elements that were inserted.  Click on\n\"Elements\" in that Developer Tools tab that opened at the bottom of\nyour window.  Then click the magnifying glass, and click on the\n`Snippets` menu.  This will jump the Developer Tools to the part of\nthe source with that menu.  Scroll through to find the menu item\nthat's not working correctly, and take a look at it.  The text in the\n`data-snippet-code` attribute is especially important, since that's\nwhat gets inserted into the notebook.\n\n\nTODO\n----\n\nThere's a bunch of stuff I still need to do, listed in\nthe\n[issue tracker](https://github.com/moble/jupyter_boilerplate/issues).\nIf you find a bug or have an idea for a good snippet that you think\nshould be added to the defaults, feel free\nto\n[open a new issue](https://github.com/moble/jupyter_boilerplate/issues/new).\n\nIn particular, I don't use Julia or R, so I welcome suggestions for\ndefault snippets for those languages.\n\n\n\nReasons for using this extension\n--------------------------------\n\nThis is just a nice place to collect thoughts about why anyone might\nfind this to be a useful extension:\n\n  * Introducing beginners to coding.  It's helpful for the beginner to have a\n    list of useful possibilities (with correct syntax!) lined up right where\n    the programming is happening.  It's like \"Hello world\" on steroids.\n  * Introducing the Jupyter notebook.  It can be useful to have some nice\n    things to do in the notebook to demonstrate the possibilities.  For\n    example, you might suggest that someone new to the notebook run the\n    Matplotlib setup and then make an example plot.\n  * Convenience for lazy people like me.  For example, I usually don't want to\n    bother with all the typing involved in setting up the nice (but important)\n    parts of a plot, like the axis labels and legend.  But by inserting the\n    template, all I have to do is change the relevant values.\n  * Reminders about useful things that could be done.  For example,\n    when I'm manipulating expressions in SymPy, I'll frequently forget\n    that I can simplify, expand, collect, etc., in all sorts of ways.\n    The Snippets menu reminds me of that.\n  * Convenient reference for massive libraries.  For example, SciPy contains\n    lots of constants.  You could certainly go to the web page describing these\n    various constants to find the one you need, or you could just explore them\n    right in the browser.  The same is true of SymPy's collection of special\n    functions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoble%2Fjupyter_boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoble%2Fjupyter_boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoble%2Fjupyter_boilerplate/lists"}