diff --git a/panel/io/notebook.py b/panel/io/notebook.py index ae5dee8afe9..36a209fa1b7 100644 --- a/panel/io/notebook.py +++ b/panel/io/notebook.py @@ -44,7 +44,7 @@ from .embed import embed_state from .model import add_to_doc, diff from .resources import ( - PANEL_DIR, Resources, _env, bundle_resources, patch_model_css, + PANEL_DIR, Resources, _env, bundle_resources, patch_model_css, CDN_DIST, DIST_DIR ) from .state import state @@ -184,6 +184,32 @@ def render_template( ) return ({'text/html': html, EXEC_MIME: ''}, {EXEC_MIME: {'id': ref}}) + +def _visit_json_data(data, parent=None, key=None): + if parent is not None: + yield data, parent, key + typ = type(data) + if typ is dict: + for k, v in data.items(): + yield from _visit_json_data(v, data, k) + elif typ in {list, tuple, set}: + for i, v in enumerate(data): + yield from _visit_json_data(v, data, i) + + +def patch_inline_css(doc_data): + for data, parent, key in _visit_json_data(doc_data): + if type(data) is dict: + if 'type' in data and 'name' in data and data['name'] == 'ImportedStyleSheet' and data['type'] == 'object': + url = data['attributes']['url'] + if url.startswith(CDN_DIST): + path = DIST_DIR / url.replace(CDN_DIST, '') + if path.exists(): + css = path.read_text(encoding='utf-8') + data['name'] = 'InlineStyleSheet' + data['attributes']['css'] = css + del data['attributes']['url'] + def render_model( model: 'Model', comm: Optional['Comm'] = None, resources: str = 'cdn' ) -> Tuple[Dict[str, str], Dict[str, Dict[str, str]]]: @@ -200,6 +226,10 @@ def render_model( model.document._template_variables['dist_url'] = dist_url (docs_json, [render_item]) = standalone_docs_json_and_render_items([model], suppress_callback_warning=True) + + if resources == 'inline': + patch_inline_css(docs_json) + div = div_for_render_item(render_item) render_json = render_item.to_json() requirements = [pnext._globals[ext] for ext in pnext._loaded_extensions @@ -239,12 +269,15 @@ def render_mimebundle( model: 'Model', doc: 'Document', comm: 'Comm', manager: Optional['CommManager'] = None, location: Optional['Location'] = None, - resources: str = 'cdn' + resources: str = None ) -> Tuple[Dict[str, str], Dict[str, Dict[str, str]]]: """ Displays bokeh output inside a notebook using the PyViz display and comms machinery. """ + if resources is None: + from ..config import config + resources = 'inline' if config.inline else 'cdn' # WARNING: Patches the client comm created by some external library # e.g. HoloViews, with an on_open handler that will initialize # the server comm. diff --git a/panel/io/resources.py b/panel/io/resources.py index 8e893437dd3..51fd7f5085a 100644 --- a/panel/io/resources.py +++ b/panel/io/resources.py @@ -816,8 +816,59 @@ def js_raw(self): # Inline local dist resources js_files = self._collect_external_resources("__javascript__") self.extra_resources(js_files, '__javascript__') + skip_import_map = None + inlined_requires = {} + def get_inlined_js(url): + nonlocal skip_import_map + if skip_import_map is None: + from .notebook import require_components + _, _, exports, skip_imports = require_components() + export_to_module_map = {v:k for k,v in exports.items()} + skip_import_map = {} + for k, js_files in skip_imports.items(): + for js_file in js_files: + skip_import_map[js_file] = k, export_to_module_map.get(k, k) + path = DIST_DIR / url.replace(CDN_DIST, '') + js = path.read_text(encoding='utf-8') + if url in skip_import_map: + export, modname = skip_import_map[url] + inlined_requires[modname] = export + js = ''' + //console.log('in inlined script'); + let define_orig = this.define; + const fnthis = this; + const fndefines = []; + this.define = function define(...args){ + //console.log('define args:', args) + if (typeof args[0] == "string" ) { + return define_orig.apply(fnthis, args); + } else { + fndefines.push(%(modname)r); + //console.log('defining %(modname)s'); + return define_orig.apply(fnthis, [%(modname)r].concat(args)); + } + }; + this.define.amd = define_orig.amd; + try { + //console.log('loading ', %(modname)r, this, this.define); + %(js)s; + } finally { + fnthis.define = define_orig; + //console.log('fndefines:', fndefines); + for (var mod of fndefines) { + require([mod], function(exp){ + if (mod == %(modname)r) { + fnthis[%(export)r] = exp; + } + }) + } + } + ''' % dict(js=js, export=export, modname=modname) + return js + raw_js += [ - (DIST_DIR / js.replace(CDN_DIST, '')).read_text(encoding='utf-8') + get_inlined_js(js) + # (DIST_DIR / js.replace(CDN_DIST, '')).read_text(encoding='utf-8') for js in js_files if is_cdn_url(js) ] diff --git a/panel/models/layout.ts b/panel/models/layout.ts index ba3cd3f6020..939398f8445 100644 --- a/panel/models/layout.ts +++ b/panel/models/layout.ts @@ -47,7 +47,7 @@ export class PanelMarkupView extends WidgetView { } } if (Object.keys(this._initialized_stylesheets).length === 0) { - this.style_redraw() + setTimeout(() => {this.style_redraw()}, 1); } } @@ -175,6 +175,9 @@ export abstract class HTMLBoxView extends LayoutDOMView { }) } } + if (Object.keys(this._initialized_stylesheets).length === 0) { + setTimeout(() => {this.style_redraw()}, 1); + } } style_redraw(): void {