11import { JupyterFrontEnd , JupyterFrontEndPlugin } from '@jupyterlab/application' ;
22import { INotebookTracker } from '@jupyterlab/notebook' ;
3+ import { INotebookContent } from '@jupyterlab/nbformat' ;
34import { SidebarIcon } from '../ui-components/SidebarIcon' ;
45import { EverywhereIcons } from '../icons' ;
56import { ToolbarButton , IToolbarWidgetRegistry } from '@jupyterlab/apputils' ;
67import { DownloadDropdownButton } from '../ui-components/DownloadDropdownButton' ;
78import { Commands } from '../commands' ;
89import { SharingService } from '../sharing-service' ;
9- import { INotebookContent } from '@jupyterlab/nbformat ' ;
10+ import { VIEW_ONLY_NOTEBOOK_FACTORY , IViewOnlyNotebookTracker } from '../view-only ' ;
1011
1112export const notebookPlugin : JupyterFrontEndPlugin < void > = {
1213 id : 'jupytereverywhere:notebook' ,
1314 autoStart : true ,
14- requires : [ INotebookTracker , IToolbarWidgetRegistry ] ,
15+ requires : [ INotebookTracker , IViewOnlyNotebookTracker , IToolbarWidgetRegistry ] ,
1516 activate : (
1617 app : JupyterFrontEnd ,
1718 tracker : INotebookTracker ,
19+ readonlyTracker : IViewOnlyNotebookTracker ,
1820 toolbarRegistry : IToolbarWidgetRegistry
1921 ) => {
20- const { commands, shell } = app ;
21- const contents = app . serviceManager . contents ;
22+ const { commands, shell, serviceManager } = app ;
23+ const { contents } = serviceManager ;
2224
2325 const params = new URLSearchParams ( window . location . search ) ;
2426 let notebookId = params . get ( 'notebook' ) ;
@@ -41,12 +43,15 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
4143 console . log ( 'Retrieving notebook from API...' ) ;
4244
4345 const notebookResponse = await sharingService . retrieve ( id ) ;
44- console . log ( 'API Response received:' , notebookResponse ) ; // debug
46+ console . log ( 'API Response received:' , notebookResponse ) ;
4547
46- const content : INotebookContent = notebookResponse . content ;
48+ const { content } : { content : INotebookContent } = notebookResponse ;
4749
48- // We make all cells read-only by setting editable: false
49- // by iterating over each cell in the notebook content.
50+ // We make all cells read-only by setting editable: false.
51+ // This is still required with a custom widget factory as
52+ // it is not trivial to coerce the cells to respect the `readOnly`
53+ // property otherwise (Mike tried swapping `Notebook.ContentFactory`
54+ // and it does not work without further hacks).
5055 if ( content . cells ) {
5156 content . cells . forEach ( cell => {
5257 cell . metadata = {
@@ -56,27 +61,31 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
5661 } ) ;
5762 }
5863
64+ const { id : responseId , readable_id, domain_id } = notebookResponse ;
5965 content . metadata = {
6066 ...content . metadata ,
6167 isSharedNotebook : true ,
62- sharedId : notebookResponse . id ,
63- readableId : notebookResponse . readable_id ,
64- domainId : notebookResponse . domain_id
68+ sharedId : responseId ,
69+ readableId : readable_id ,
70+ domainId : domain_id
6571 } ;
6672
67- // Generate a meaningful filename for the shared notebook
68- const filename = `Shared_${ notebookResponse . readable_id || notebookResponse . id } .ipynb` ;
73+ const filename = `Shared_${ readable_id || responseId } .ipynb` ;
6974
7075 await contents . save ( filename , {
7176 content,
7277 format : 'json' ,
7378 type : 'notebook' ,
79+ // Even though we have a custom view-only factory, we still
80+ // want to indicate that notebook is read-only to avoid
81+ // error on Ctrl + S and instead get a nice notification that
82+ // the notebook cannot be saved unless using save-as.
7483 writable : false
7584 } ) ;
7685
7786 await commands . execute ( 'docmanager:open' , {
7887 path : filename ,
79- factory : 'Notebook'
88+ factory : VIEW_ONLY_NOTEBOOK_FACTORY
8089 } ) ;
8190
8291 console . log ( `Successfully loaded shared notebook: ${ filename } ` ) ;
@@ -125,8 +134,11 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
125134 label : 'Notebook' ,
126135 icon : EverywhereIcons . notebook ,
127136 execute : ( ) => {
137+ if ( readonlyTracker . currentWidget ) {
138+ return shell . activateById ( readonlyTracker . currentWidget . id ) ;
139+ }
128140 if ( tracker . currentWidget ) {
129- shell . activateById ( tracker . currentWidget . id ) ;
141+ return shell . activateById ( tracker . currentWidget . id ) ;
130142 }
131143 }
132144 } ) ;
@@ -135,24 +147,26 @@ export const notebookPlugin: JupyterFrontEndPlugin<void> = {
135147 app . shell . activateById ( sidebarItem . id ) ;
136148 app . restored . then ( ( ) => app . shell . activateById ( sidebarItem . id ) ) ;
137149
138- toolbarRegistry . addFactory (
139- 'Notebook' ,
140- 'downloadDropdown' ,
141- ( ) => new DownloadDropdownButton ( commands )
142- ) ;
143-
144- toolbarRegistry . addFactory (
145- 'Notebook' ,
146- 'share' ,
147- ( ) =>
148- new ToolbarButton ( {
149- label : 'Share' ,
150- icon : EverywhereIcons . link ,
151- tooltip : 'Share this notebook' ,
152- onClick : ( ) => {
153- void commands . execute ( Commands . shareNotebookCommand ) ;
154- }
155- } )
156- ) ;
150+ for ( const toolbarName of [ 'Notebook' , 'ViewOnlyNotebook' ] ) {
151+ toolbarRegistry . addFactory (
152+ toolbarName ,
153+ 'downloadDropdown' ,
154+ ( ) => new DownloadDropdownButton ( commands )
155+ ) ;
156+
157+ toolbarRegistry . addFactory (
158+ toolbarName ,
159+ 'share' ,
160+ ( ) =>
161+ new ToolbarButton ( {
162+ label : 'Share' ,
163+ icon : EverywhereIcons . link ,
164+ tooltip : 'Share this notebook' ,
165+ onClick : ( ) => {
166+ void commands . execute ( Commands . shareNotebookCommand ) ;
167+ }
168+ } )
169+ ) ;
170+ }
157171 }
158172} ;
0 commit comments