Syntax highlighting Widget | How To publish / contribute Widgets

Hello Guys,

I created an higlightJS code widget and want to share/contribute it to the community what is the best way for that? Should i create an pull request?

Here in this documentation Custom extensions — Bokeh 3.3.2 Documentation Pre-built extensions are mentioned but i don’t realy get it :frowning:

In this documentation the css styling is given via the __css__ attribute

class LatexLabel(Label):
    """A subclass of the built-in Bokeh model `Label` that supports
    rendering LaTeX with the KaTeX typesetting library.
    """
    __javascript__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"
    __css__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
    __implementation__ = """
    # do something here
    """

As far as i tried giving the CSS stylsheets in this manner dosn’t work for Bokeh>2.4.3 there i had to embed the css in typeScript Code like in this example (Adding a custom widget — Bokeh 3.4.0.dev3 Documentation) is my understanding correct?

Here is the codeWidget Code

from bokeh.io import show, output_file
from bokeh.core.properties import String
from bokeh.models import Markup
from bokeh.layouts import column
from bokeh.models import ImportedStyleSheet
TS_CODE="""import { Markup, MarkupView } from "models/widgets/markup"
import { pre } from "core/dom"
// HTML construction and manipulation functions
import {ImportedStyleSheet,StyleSheetLike} from "core/dom"
import * as p from "core/properties"
//import hljs from "highlight.js"
declare const hljs: any;  // This line is crucial


export class CodeView extends MarkupView {
  declare model: Code
  override stylesheets(): StyleSheetLike[] {
    return [
      ...super.stylesheets(),
      new ImportedStyleSheet("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/styles/default.min.css")
    ]
  }
  override render(): void {
    super.render()

    const codeElement = document.createElement('code')
    codeElement.className = `language-${this.model.language}`
    codeElement.textContent = this.model.code

    hljs.highlightElement(codeElement)

    const content = pre({ style: { overflow: "auto" } }, codeElement)
    this.markup_el.appendChild(content)
  }
}

export namespace Code {
  export type Attrs = p.AttrsOf<Props>

  export type Props = Markup.Props & {
    code: p.Property<string>
    language: p.Property<string>
  }
}

export interface Code extends Code.Attrs {}

export class Code extends Markup {
  properties: Code.Props

  constructor(attrs?: Partial<Code.Attrs>) {
    super(attrs)
  }

  static {
    this.prototype.default_view = CodeView

    this.define<Code.Props>(({String}) => ({
      code: [ String ],
      language: [ String, "python" ],
    }))
  }
}
"""
class Code(Markup):
    __javascript__ = ["https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"]
    __implementation__ = TS_CODE
    code = String(default="")
    language = String(default="python")
    #stylesheet = ImportedStyleSheet(url="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.0/styles/default.min.css")


# Create an instance of the Code widget
code_widget = Code(code=TS_CODE, language="typeScript")

# Use output_file to direct the output to an HTML file (optional)
output_file("code_widget_example.html")

# Create a simple layout
layout = column(code_widget)


# Display the layout
show(layout)

Hi @BeSeeTek I’m not entirely sure what you are asking — there seem to be multiple questions. Some quick comments:

  • There’s no central collection of user extensions. BokehJS is still under very heavy active internal development, and is still just too much a moving target for that to be feasible yet.
  • It might make sense to contribute something as an example or to include in the docs, if it demonstrates something new that isn’t already covered (it’s not clear what your extension does or is)
  • pre-built extensions are useful in the Bokeh server case, so that the cost of compiling the extension is only paid once, rather than repeated on every new session load.

Hi @Bryan tank you for your super fast response.

Your remarks helps me lot.

  • Ok i hopes there would be a user collection. But i understand your point.

I tried to day to implement and widget witch does syntax highlighting doe source code, but maybe just using an markdown Field would do the job as well, and so my widget is not so interesting.

class LatexLabel(Label):
    """A subclass of the built-in Bokeh model `Label` that supports
    rendering LaTeX with the KaTeX typesetting library.
    """
    __javascript__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js"
    __css__ = "https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css"
    __implementation__ = """
    # do something here
    """

is added to the root of the page and is not applied to the widget :frowning: this is why i had to integrate it into the TypeScript but this seems to be the correct way since the newest examples do it this way.

In this documentation an example gallary containing something with LaTeX is mentioned For a complete implementation and its output, see the LaTeX example in the extension gallery below.
but i can´t find this most likely this part of the documentation is outdated. In the 2.4.3 Version of this documentation the LaTeX example was present.

  • Regarding the prebuild extension: Do i get it right that every time an user connects to the bokeh server the typeScript is recompiled ? Or everytime i restart the server app, this would be okay, every time not. So i will try bokeh init --interactive to build all the files?

best regards Bene