Itay Dafna
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Jupyter Community Workshop - The Future of Jupyter Widgets ## Tuesday 18th of October - Friday 21st of October, London ## Week Agenda: [Google Docs Agenda](https://docs.google.com/spreadsheets/d/1KMB3Mda1MNVXzjSm30eNO7qIW7Oj2z3nYBFY5l-sZ0A/edit#gid=998190500) ## Friday 2022-10-21 Zoom link for remote attendees: https://bloomberg.zoom.us/j/95137727255?pwd=OGpmYmI0RkJZelk1V2ErUWoyc2w4QT09 * 8:30-9:30: Breakfast * 9:30-12:00: Knowledge sharing / Hacking * 12:00-13:00: Lunch * 13:00-18:00: Socials / Hacking! ipywidgets overview - package overview: - js - base/widget.ts -> basic widget class - base-manager -> inherts from base - html manager -> based on base manager - schema -> useful documenation around - python - ## Thursday 2022-10-20 Zoom link for remote attendees: https://bloomberg.zoom.us/j/92527562243?pwd=ODFuR2VlZjVia0xLcCtPemRhUkFPUT09 * 8:30-9:30: Breakfast * 9:30-12:00: Building larger applications using ipywidgets (with discussion of performance and developer experience) * 12:00-13:00: Lunch * 13:00-17:00: Hacking! * 17:00-18:00: Demos * 18:30 (optional): Impromptu drinks/dinner ## Wednesday 2022-10-19 Zoom link for remote attendees: https://bloomberg.zoom.us/j/97006618705?pwd=SnM3OXdWYUtDbWUwbFZZenY5S2ZWZz09 * 8:30-9:30: Breakfast * 9:30-12:00: Reviewing the entire ipywidgets stack * 12:00-13:00: Lunch * 13:00-17:00: Hacking! * 17:00-18:00: Demos * 18:30 (optional): Dinner @ Apulia (https://apuliarestaurant.co.uk/) ## Tuesday 2022-10-18 Zoom link for remote attendees: https://bloomberg.zoom.us/j/93920480098?pwd=SnkvNGlCWTZiUGY4b3EzWlNMMHEydz09 15 Attendees representing academia and industry, with core developers, third-party widget developers, widget users, etc. * 8:30-9:30: Breakfast * 9:30-12:00: Introductions and demos * 12:00-13:00: Lunch * 13:00-17:00: Discussion about widgets in various frontends * 17:00-18:00: Demos * 18:30 (optional): Drinks at The Last Talisman ### Demos * Maarten Breddels on [Reacton](https://github.com/widgetti/reacton) * Provides an answer for how to build large applications in ipywidgets, basically a port of React to Python, which uses ipywidgets, etc., as the equivalent of the DOM elements * Wrapped ipywidgets, and can autogenerate wrapper as long as constructor arguments match the traits. Currently ipywidgets, ipycanvas, bqplot, wrappers are shipped with reacton * Compare with idom: idom is driven by react. Reacton does everything in Python * How to create a new wrapped react component? Create a widget, then generate the wrapper in Reacton. * How do we embed an ipywidget into a React application? * Maarten does embed into Vue * We could have better examples for embedding an ipywidgets into existing web applications * You can embed reacton inside an ipywidgets app, and embed ipywidgets inside a reacton app * [Max Fordham](https://www.maxfordham.com/): [ipyautoui](https://github.com/maxfordham/ipyautoui) project * Generates a ui based on pydantic types * Nate Rush on [mito](https://www.trymito.io/) * Jeremy Tuloup, Martin Renou: * JupyterLite * Jupyter Notebook 7 (based on JupyterLab components) * [JupyterLab App Cookiecutter](https://github.com/jupyterlab-contrib/jupyterlab-app-cookiecutter) * https://xeus-python-kernel.readthedocs.io/en/latest ### Discussion: Widgets in Other Locations * Don Jayamanne on ipywidgets in VS Code * https://github.com/microsoft/vscode-jupyter/wiki/IPyWidget-Architecture-Challenges * Details of how VS code handles message passing from frontend to kernel * Widget echos: https://github.com/jupyter-widgets/ipywidgets/pull/3195 * Pete Blois on colab: * How colab handles this: https://github.com/googlecolab/colabtools/blob/main/google/colab/output/_widgets.py * [Finding all dependencies of a set of widgets including jslink](https://github.com/jupyter-widgets/ipywidgets/pull/3114/files#diff-346aff842b126a18502cf33a55d587dbfe6c963c9991cf9f9bb56e205b88e41eR299) * Issue in ipycanvas where there was a missing ref between widgets https://github.com/martinRenou/ipycanvas/pull/290 resulting in iframed canvases failing to render. ### Issues for working groups * iframe isolation for widgets * lightweight kernel manager that can support multiple versions of the widget manager * output widgets, and how outputs are updated (kernel vs frontend) * ipywidgets 7+8 * Want to support ipywidgets 7 core controls in ipywidgets 8 widget manager * Layering the apis in the frontend to separate concerns: * synchronization * model instantiation * view rendering * Clearly documenting the entry points for AMD modules (like in JLab has metadata in package.json) * Provide a RESTful end point to expose the list of widgets and their corresponding entry points in JLab and Jupyter Notebook. * Rich outputs without Widgets (HTML & Comms) * Freezing part of the stack to give predictability and prevent breakageshttps://github.com/jpmorganchase/jupyterlab_templates * Testing * currently have unit tests and some Galata ui tests * expand to more comprehensive protocol end-to-end protocol tests * * ipywidgets in other non-notebook contexts: streamlit, solara, shiny * ipywidgets as a protocol? * Moving the documentation (which exists in an npm package somewhere) to the docs * Should the kernel handle the Output widgets redirection? * error handling for comm messages that don't get through * Handling the "comm target does not exist". Should we [improve the jupyter kernel protocol](https://github.com/jupyter/enhancement-proposals/issues/86)? * [Remove the ipykernel and IPython dependencies](https://github.com/jupyter-widgets/ipywidgets/issues/3514): * Rely on a Comm modules that's separated from ipykernel: https://github.com/martinRenou/comm / https://github.com/jupyter-widgets/ipywidgets/pull/3533 / https://github.com/ipython/ipykernel/pull/973 / https://github.com/jupyter-xeus/xeus-python/issues/342 * ipywidget author education: * Finding documentation in the first place * Custom widget dev tutorials * Checking for broken links / tutorials * Run codespell on the docs/code-base * Linting: https://github.com/jupyter-widgets/ipywidgets/pull/3566 * Packaging: https://github.com/jupyter-widgets/ipywidgets/pull/3587 * async/await: https://github.com/jupyter-widgets/ipywidgets/pull/2779 * What problems are we solving with widgets vs. what we set out to solve: * devs who rely on the packaging aspect: widgets as a ways to distribute to multiple UIs * users who rely on widgets: quick and dirty prototypes vs. full applications (or, as a gateway from one to the other) ### End of day Demos * Matt Craig: Demoed * Templating extensions: * https://pypi.org/project/jupyterlab-multicontents-templates/ * https://github.com/jpmorganchase/jupyterlab_templates ([pypi](https://pypi.org/project/jupyterlab-templates/)) * https://github.com/trungleduc/jupyter_app_launcher * https://jupyterstarters.readthedocs.io/en/stable/ ## Wednesday, Oct 19 ### Full ipywidgets stack discussion #### Traitlets discussion - pydantic (typing) -> value: Syncable[Int] - Can we rebuild trailets on top of pydantic? - Outside our ecosystem, so is it worth the risk? - Perhaps the best bet is just improving traitlets - We don't have to abondon trailets for widget authors; we can let widget users use pydantic - Refactor traitlets: - Performance: - Creating traitlets with static default values can be improved 20x (maarten has working PR) - Event processing: a lot of time appears to be spent in `bunch` - Exposing typing to static typers - Removing the metaclass that attaches the name to each traitlet - [pep 487](https://peps.python.org/pep-0487/) - [Adding typing using dataclass transform](https://peps.python.org/pep-0681/) #### JS Stack #### Backbone - Uses: - Eventing - Framework for instantiating and syncing - Equality comparison (deciding if you need to sync something); _.isEqual - Lifycycle - State of Backbone as of October 2022: - There is a pinned issue on the repo stating the project is still maintained: https://github.com/jashkenas/backbone/issues/4244#issuecomment-991381707 - There was a release on February 26th 2022: https://backbonejs.org/#changelog - So there *might* not be a need to fork and maintain a custom Backbone *for now* - Issues with backbone: - jquery: exposed as part of the API for backwards compatibilty - Would be nice to remove this as it's not used by jupyter - How to remove this without breaking custom widgets, unclear - $ as a property, first time you access it, it loads? - We don't fit the syncronization model, have had to strip a lot of this - Maintainablity: it's fossilized, hard to get changes merged. - Maarten had issue getting equality checking for typed arrays merged - The goal perhaps is removing jquery - It's very hard to do a lazy-load of jquery, as these are syncronous APIs - Removing jquery is far less of a breaking change than ipywidgets 7 -> ipywidgets 8 - It's like 50kb for 2022. The modern browser versions are super small, as they punt to the browser for everything - What's even the goal of getting rid of jquery? - The biggest thing you get is the a simpler interface - reimplementing widgets is easier - Options: - Fork backbone, have more control, and we can get rid of jquery - Leave in all the syncing stuff, etc - in case people rely on this - Could remove the collections, etc - What would it take to drop backbone? - If we had lumino widget class from the start, would we have used backbone? - Backbone has a more nuianced event system: e.g. the value change events, there's more advanced logic here - Changing the event handling is high-risk - e.g. ipythreejs relies on specific event ordering - Forking the portion of backbone that does this event handling? - Issues with typing? Only for when widget authors upgrade, so not the worst sort of breaking change - Could we just have a strategy for when backbone fails us? - E.g. if you could put in a custom equals, this typed array equality comparison would be ok - Deprication messages? - When you use $ on the page - In webpack, we could log a warning - On ipywidgets 7 -> 8: - This could be a test case for if widget authors are willing to upgrade - Potentially, the upgrade fatigue will just build up - Maybe we should insist on being future compatible and backward compatible #### Lumino - lumino widget - processLuminoMessage - Can we rely on the browser APIs? - before attach, after attach - onresize, presumably you could shim this to the the browsers resize event w/ ResizeObserver - Do widgets need to use lumino? - Not necessarily - Questions around keeping the API backward compatible + a smaller subset stable API? - A working group looks at the API, decides what a minimal API looks like for building widgets. Can we build a large portion of the widgets going forward just with this smaller API? - Two approaches: - Slimming down the widgets API - Starting from scratch - Lumino is going to be around for as long as we use it to build the frontends ; there's less concern than JQuery - Working group on widget API changes: - Option 1: removing things (e.g. jquery, Lumino) from the API - Option 2: reccomending a specific subset of the API that will be long-term maintained - Option 3: keeping the API the same, and then adding a new API that is long-term maintained - Etc. - Dicsussion on the above working group: - removing things from existing API gets less pressing if we introduce a new minimal subset API that users can rely on - although one of the things we're loosing is composability of widgets if we just have this minimal API - How much composition of indivigual outputs really occurs? - The layout widgets are extremly common - if different people do it different ways, then there will be libraries that effectively do this widget management - The hard part of the minimal API is the composability of the widgets: - But if both widgets are syncing back to Python, then you can still do the side-by-side widgets - But this is where jslink becomes an issue, but maybe this can be exposed as an API - How do widgets communicate on the frontend: - Pete argues that this different frameworks do this differently, and trying to be opinioned on this in a long term contract is a loosing game. - Don points out that this means that widgets interoperating requires widget authors to opt-in in ways that they don't - Plan: punt the discussion to this afternoon, and come up with a concrete proposal - A group of people have a more concrete proposal: - Here's the API - Here's how we handle embeding a widget inside another widget - Here's how they talk to eacother on the front-end / backend - Here's how a widget would work with the current stack - Here's how a widget that doesn't work with the current stack would work (and here's how it communicates with the current stack) #### Webpack - This is an internal detail of widgets: - We could benefit from using another technology - esbuild: - Does es6 module output - We put a checkmark on this, and move on. We're not gonna consider this one now #### AMD modules - Current cookiecutter, there are currently a federated module for jupyterlab, and then two amd modules (for notebooks and for cdn) that look pretty much the same. - Options and benefits: - es6 modules are the modern one, most things are moving to this. The primary benefit is that it's a bit more standarized. - When loading as an es6 module, async dependencies, this is standardized. - This is the fully standardized API. - Potentially prefering this becuase this is the browser standard - What would a managed transition to es6 look like? - What steps are the breaking changes, how to make them less painful? - If we're going to go to the minimal API, then we can leave AMD modules in the old API - Aka, one option is just adding the es6 as another module to the webpack - What is the carrot for widget authors? This allows them to load these modules from a CDN (jlab would need to support this?) - This question is about: how do widget managers pull in the widgets? - Don, pete agree that the important this is that we all agree on how to load the widgets - What non-jupyter notebooks do: - Collab has their own impl of require js that avoids polluting the global namespace - Microsoft used to have their own require js for .ipynb, but moved to just having standard require js in the webview last week - Working group for looking into ES6 - Answering two questions: - What are the benefits, if any? - Is it feasible to do it? - Major question around external modules - need some external configuration in the bundler or something - If there are benefits, and it is feasible, then could consider adding es6 modules as another bundle? - Grant Nestor had a demo of widget modules as es6 modules - What would you love to see in widgets? ### Minimal Interactive Outputs API - Previous brainstorming: https://github.com/Quansight-Labs/jupyter-output-spec - Colab's stable API: https://github.com/googlecolab/colabtools/blob/main/packages/outputframe/lib/index.d.ts - How can this be integrated into an improved `%%javascript` magic? https://github.com/ipython/ipython/issues/13376#issuecomment-989040562 - Examples: https://github.com/blois/js-module-renderer/blob/master/tests.ipynb ### Working Groups #### Minimal API Attendee: Pete, Vidar, Paul, Jeremy, Jason An es6 module that exports a render method taking a context argument. A context has a discovery function and functions for: - comms - update output state - Colab has a comms api: - `open("target"): Promise<Comm>` An example mimebundle: ```json { 'application/vnd.jupyter.es6-rich-output': 'a string that will be the src of a script tag, i.e., a url, data url, etc.', 'application/vnd.jupyter.datagrid+json': {'data': ['some', 'data', 'in', 'a', 'structure', 'which', 'the', 'js', 'can', 'access']}, 'text/html': 'fallback rendering of your data', 'text/plain': 'fallback rendering of your data' } ``` The mimebundle renderer for `application/vnd.jupyter.es6-rich-output` does a System.import of the url as an object, then calls the render method if it exists with a context object, awaiting the returned `Promise<void>` before moving to the next output. The context object has mandatory fields: - `output`: {'data': {...}, 'metadata': {...}} (which is the display_data or execute_result message data/metadata fields). Changes are not respected - treat it as immutable. - Should the `transient` data also be passed in??? - `element`: HTMLDivElement Optional fields: - `render(output, element): Promise<void>`: - `output`: a kernel iopub stream, display_data, execute_result, error message (i.e., inputs into the rendering system) - Should these be formatted for nbformat (e.g., transient field is not included)??? Should they include the trusted field that is passed in from JLab??? - `element`: an HTMLDivElement - Promise resolves when the rendering callback returns - `comms`: See Colab interface... - `Comm` interface - `open("target")`: - `sharedState`: Map([@@Symbol]: Any) - shared state that might span render calls or contexts. This is considered mutable, in that a renderer can share things with other render calls, replace implementations, etc. - Let's explore exactly the scope of shared state (e.g., per kernel, per document, etc.). Does there need to be different scopes, or a way to introspect the scopes, etc.??? - JupyterLab branch for prototype implementation: https://github.com/jupyterlab/jupyterlab/tree/es6-rich-output #### Traitlet improvements - Integration with more natural typing - ? - Removing the metaclass - ? - Performance improvements https://github.com/ipython/traitlets/pull/788 ### Thursday #### Building larger applications with ipywidgets - Initial load performance? - There are hard limits on the - Server-side rendering - Doing some things client-side with a wasm runtime, doing what you need on the backend - Could we statically figure out which is which? - Performance: - Users sometimes end up with hundreds to thousands of widgets, pushing memory usage (mostly on Python?) - Where are the performance bottlenecks with hundreds of widgets? - Where is the time spent with creating composition lots of widgets? - How does creating 100 button widgets compare to creating 100 normal html buttons -- what is the overhead? - Solara: - Viola slower than and solara on initial load, due to starting a different Python process per - Potential benchmarks: - 100 widget buttons vs. 100 HTML buttons - Where is memory used for hudreds/thousands of widgets that are all visible? - On scaling application complexity: - Why do applications get complex? - No seperation of concerns - n^2 communication problem - Callback hell - There are no state management libraries for Python - It's also not clear that these Python users are looking for state management solutions - What are the user profiles who are using ipywidgets? - Recent college grad: goes to a bank, starts writing Pythonm uses ipywidgets to create an application. Not a software engineer, etc - it grows organically. - This is an iterative proces. Takes week if not months to work through issues. - E.g. memory that hangs around, this is really slow. There is no notion of lifecycle, etc. - More typical quants: they are using Jupyter for more prototyping stuff - This conversation is very similar to notebook vs. raw Pyhton files. It's very easy to get to a place in your code development where it's extremly hard to figure out what state you have vs. not, and then u.r.done.... - Potential documentation changes: - Should we have a section on the application page like: you built your first app. - Perhaps we should think about it as "how can we allow users to just copy and paste and it just works" - Potentially a clever opinionated way of a way of building applications: - Sometimes this gets built internally within companies - - This could be something that we distribute, and allow users to use - Ideas: - Class with inheritance? - Reacton - Providing a visual programming interface? We could give a GUI builder - Concerns about degree of customizability - Doc changes: - More complex examples of building complex widget applications - Testing and typing: - Good practices for testing (although people might not use it) - Static typing wherever possible - Working groups: - Proper state management for - Perhaps form input, simple output, that you regenerate - Show an example of building small components that then transitions to composing them together - Create a `contrib` organization on GitHub, similar to `jupyterlab-contrib`: - There could be a list of custom widgets like on the `jupyterlab-contrib` website: https://jupyterlab-contrib.github.io/extensions.html - The [Jovyan](https://github.com/jovyan) github org is also a place for Jupyter community projects. See https://github.com/jupyter/docker-stacks/issues/358 for more info about the name "Jovyan". - The name `jupyter-widgets-contrib` could make it more explicit about what it is about? And easier to find via search engines. It would also be similar to the existing `jupyterlab-contrib`. ### Friday #### Contributing + Walk-through of fixing a [bug in ipywidgets](https://github.com/jupyter-widgets/ipywidgets/issues/3597). + Installation along the way: wrong (old) version of jupyterlab installed when creating conda environment. Known issues, see: https://github.com/mamba-org/mamba/issues/1581 #### Yjs investigations + https://github.com/jupyter-widgets/ipywidgets/pull/3195 already helped quite significantly to keep the state synced between the kernel and multiple frontends + *Enabled* by default in 8.x: https://github.com/jupyter-widgets/ipywidgets/blob/e3d8fe576334dc84d24ba8b20b68423e9e72ffe7/python/ipywidgets/ipywidgets/widgets/widget.py#L42 + *Disabled* by default in 7.x: https://github.com/jupyter-widgets/ipywidgets/blob/892166dd0524e93b912d8826a6da26c09afd4366/ipywidgets/widgets/widget.py#L49 + Looking into using `yjs` / `ypy` for widgets: + probably the simplest for now would be to start a completely separate project rather than trying to replace the internals of ipywidgets directly + There could be an ephemeral `YDoc` created in the kernel + A first step could be to create a new `Websocket` connection between the web clients (JS) and the Python kernel. This would bypass the current contract of message ordering but could be fine for now for testing. There could be a simple JupyterLab extensions exposing a simple widget like the `IntSlider` via a mimerender extension + Widgets state are managed in the kernel via the `YDoc` + Ideally use a custom websocket provider over the `comms`?

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully