Skip to content

Instantly share code, notes, and snippets.

@blink1073
Created January 14, 2021 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blink1073/1c21ec077acbb9178e01e14936ddda1b to your computer and use it in GitHub Desktop.
Save blink1073/1c21ec077acbb9178e01e14936ddda1b to your computer and use it in GitHub Desktop.

Phosphor Meeting Minutes - 2019

Link to recorded videos: https://drive.google.com/drive/folders/1tfJ53NHQyequSHbYbSvs3jIFuWbcYZ9E?usp=sharing

Meeting: https://zoom.us/j/650076676

Covered Packages: algorithm collections messaging signaling disposable properties coreutils domutils keyboard commands dragdrop virtualdom

Remaining Packages: widgets application

In-development Packages: datagrid datastore

30 September 2019

Scrollbar

  • A lot of virtual scrolling implementations use a large div to create a system scrollbar. Some system scrollbars have limitations for how many items they can scroll, like a million pixels or something. For large data sets (like a trillion rows), we still need custom scrollbars.
  • Also allows styling, which has been notoriously difficult with system scrollbars
  • Used in other Widgets like DataGrid
  • Simple interface. Minimum value is 0. Maximum is settable. Page size is how many pixels to skip
  • clicking and dragging the thumb, clicking on step (arrow) buttons and clicking on track do different things
    • dragging the thumb emits the thumbMoved signal
    • clicking on a step button emits a setupRequested signal
    • clicking on scroll track emits a pageRequested signal
  • clicking and holding on track or step button will emit the related signal repeatedly
  • can style scrollbar based on data-orientation DOM field (“horizontal” vs “vertical")
  • We make sure to release the mouse holds after we detach from the DOM
  • onUpdateRequest - first tried to convert everything to pixels, but it was complicated and didn’t render well. So now we work entirely in percentages.
  • The transform in the onUpdate essentially makes the active pixel of the thumb the first pixel when scrolled to the top, the last pixel when scrolled to the bottom, and transitioning smoothly throughout the range. This is so that we don’t have to do any DOM measurements.
  • Pressing escape when dragging resets the scroll position to the start and cancels the drag (like on Windows?)
  • Step and Page moves can repeat if mouse button is held down. The repeating happens even if you move off the track, then back on. This is consistent with OS behavior (at least on macOS).
  • Questions:
    • What would it take to implement search highlights in the scrollbars
    • are they dynamically resizable?

DockLayout

DockPanel

12 August 2019

CommandPalette

  • Provides a visual way for users to view and execute commands in the application command registry (in a similar way to menus)
  • As with other leaf widgets, it takes an optional renderer. There is a default renderer, which is designed to be subclassed.
  • The renderer has functions to render headers, items, and an empty message
  • The default renderer is stateless.
  • The command palette notably does not use a hierarchichal DOM node layout for headers/items. Instead it renders them as a flat list, which should be higher performance.
  • A layout is not allowed to be added, since the palette is assumed to own all its content.
  • The API is intentionally extremely similar to that of menus.

The command palette exposes methods to remove items. This is distinct from the context menu and the command registry. Those objects are intended to be application singletons, so they deliberately have a more restrictive API, so that different plugins can’t necessarily interfere with each other.

  • All of the rendering, fuzzy rendering, etc, happens in an update request handler.
  • Keyboard modifiers are explicitly ignored by the command palette, so that it could add some handling for them in the future without breaking existing behavior
  • When a command is executed, the palette goes back to the default state, clearing any state. This has proven to be contentious, and a lot of user feedback has focused on making it jump around less.
  • An earlier version of the interface used colons in the search query to denote searching command palette headers. This was abandoned for a couple of reasons:
    • Less discoverable
    • More complex implementation
    • Subtle issues around order-of-operation The decision could be reversed, or made an extension point similar to the IRenderer
  • The fuzzy matching algorithm:
    • Case insensitive
    • Allows missing characters
    • Penalizes missing characters
    • If there’s no query, everybody matches

5 August 2019

MenuBar

  • As with menus, tabbars, you can provide a custom renderer, or subclassing the default renderer.
  • The default MenuBar.Renderer implementation is designed to be subclassed.
  • Since the MenuBar handles its own DOM layout, it disallows setting a layout, so users can’t add children through public APIs.
  • openActiveMenu() is necessary to allow application authors to open the menu even if it is not the actively focused element.
  • aboutToClose and menuRequested signals are used to propagate events between menu bars and menus/submenus so that keyboard interactions work as expected.
  • In many cases, phosphor attaches event listeners with a life cycle (e.g., drag/drop), so they are not always active. The menu bar is an exception: it captures most mouse/keyboard interactions since we expect to to be interactive basically always.

ContextMenu

  • The context menu is notably not a Widget. It is instead a class that manages creation and deletion of Menu widgets upon demand.
  • addItem returns an IDisposable that can be used to remove context menu items. In this way, only the code that adds an item has access to the disposable. This is intended to allow extension authors to keep control over the items they add.
  • The ContextMenu uses a similar system to construct the menu as the CommandRegistry uses for keyboard shortcuts. Based on the location of a click, it goes up the DOM hierarchy, testing against CSS selectors to see which commands might be enabled for a given click.

29 July 2019

Menu

- The phosphor `Menu` widget is intended to be a leaf widget, in that it is not designed to contain other widgets
- The menu renders commands from a `CommandRegistry`, including mnemonics, keyboard shortcuts, labels, etc.
- A menu can take a `Menu.IRenderer`, which can be used to customize how menus are rendered into a node. If none is provided, then a default one is used that looks reasonable. The default `Menu.Renderer` is designed to be subclassed.
- Menus automatically handle keyboard navigation around the hierarchy, including arbitrarily nested submenus, as well as the menu bar. It accomplishes this by wiring up signals for `aboutToOpen` and `aboutToClose`, which can be used by parent and child menus. Most users of the `Menu` widget won’t need to use these signals.
- Keyboard navigation also handles skipping separators and disabled menu items.
- Internal open timers and close timers handle delays for opening and closing submenus. This makes interacting with submenus less jittery.
- You don’t insert menu items directly into the menu, rather you give `Menu.I``Item``Options`, and the menu uses those to create its menu item.
If a menu item is added while the menu is open, the menu closes before updating. This should mostly not happen except if an item is added due to some asynchronous operation (JupyterLab help links are one example).
- Overflow is hidden by default for all phosphor `Widget`s. This is really important for DOM rendering performance, as it keeps reflow boundaries isolated. However, this means that menus must be attached directly to the document body so that they show up on top of everything. Thus, they are absolutely positioned with client x,y locations.
- Menus are currently not aria-compatible — there are some subtle focus issues that need to be worked out.
- The default renderer automatically collapses duplicated separators.

22 July 2019

TabPanel

- `TabPanel` is a widget that composes the `TabBar` and a `StackedPanel`
- Phosphor typically indicates a widget that has been created by another widget, and is semantically owned by that widget, with a CSS class. The stacked panel created by the tab panel is an example — it is tagged with `p-TabPanel-stackedPanel`. This allows for a somewhat more targeted class for keyboard shortcuts, context menus, etc.
- The `TabPanel` listens for a `widgetRemoved` signal on the `StackedPanel` to know when to remove a tab. This could also have been done with a message hook, but message hooks can block other message hooks, so it’s more reliable to wire up a signal connection.
- The `TabPanel`'s `TabBar` gets a dataset attribute `'``placement``'`, indicating which side of the stacked panel it is on. This allows you to target layouts with CSS.
- The `TabPanel` provides references to its underlying tab bar and stack panel, in case you need to do some operations on them that are not provided by the top-level API. However, mutating those objects may lead to undefined behavior.
- `insertWidget` automatically selects the new widget.
- Moving tabs in the tab bar will also trigger a reordering of widgets in the stacked panel. This will have no effect on the rendering, but does serve to keep the ordering of the tabs in sync with the DOM ordering of the widgets, which is useful to know when iterating over the widgets in JS.

FocusTracker

- `FocusTracker` is not itself a widget, but is a utility for tracking whether a widget is focused. It prevents the need to hook up focus events on individual widget DOM nodes.
- The distinction between `currentWidget` and `activeWidget` is a bit subtle. The docstring describes it, but briefly: the `currentWidget` is the most recently focused widget, and the `activeWidget` is the currently focused widget. So `currentWidget` may have a value even if `activeWidget` is null (indicating no widget in the set is focused).
- Items are automatically removed from the `FocusTracker` upon widget disposal, so no need to remove them when disposing of widgets.

15 July 2019

  • TabBar
  • TabPanel
    • (pushed to next week)

14 June 2019

StackedLayout (inherits from PanelLayout; recall PanelLayout is for using CSS to do the layout)

- All children lay right on top of each other; their z-order is in the same order that they were added. Tab panels use this, just making the appropriate tab “on top” in the StackedLayout.
- Another use is to have overlays, where transparency is used to see many children composed together.
- attachWidget(): Called by the base panel layout class. Sends messages, and refits the parent.
- moveWidget(): This method is important since we’re using CSS for styling, we need to ensure the internal phosphor state of the child order matches the DOM tree child order.
- detatchWidget(): Undo of the `attachWidget()` and resets the z-index (incase you reuse, we don’t leave the z-index of the “old” state).
- Fitting must happen when children are hidden, etc.
- Fitting itself: Ignore hidden children, update the overall min/max as the max of the min/max of each child. If the parent has an ancestor, it needs to be refit as well (because our min/max has changed, thus it may push around its ancestors).
- Updating: Careful not to read from the DOM size attributes more than needed, because touching the DOM at all (even just reading attributes) can cause a reflow.
  • StackedPanel (the sister class for StackedLayout)
    • Takes optional options, like every other Panel class.
    • Doesn’t add any extra API atop the base class Panel, except for one extra signal (widgetRemoved).
  • TabBar and TabPanel
    • Pushing to next week because they will take a long time.
  • SingletonLayout
    • A layout that holds a single child widget.
    • The intent is that you use CSS to layout your single child widget.
    • Not an extension of PanelLayout, instead just extends Layout.
      • Means it’s responsible for some extra things, like disposing of its single child.
      • Setting a new child widget causes the old child to be disposed (unless you first set the child widget’s parent to NULL first).
    • All layouts are iterable, thus so is this one. It just gives you one thing in an iterator.
    • Handles the lifecyle as it’s supposed to, given that it doesn’t get to steal those from the PanelLayout.
    • We don’t have the SingletonPanel yet! It would be easy to write, just needs to be done.
    • An alternative for SingletonLayout is to use a StackedLayout with just one child, or get fancy and have two children, the top one being a spinner to show loading progress, for example.
  • GridLayout (not to be confused with the DataGrid!)
    • Extends Layout, thus has to do extra work to manage children on its own.
    • This is for laying things out in a grid, like “CSS Grid” but does some other things beyond css grid (?)
    • Options to the c’tor, like normal.
    • Row counts, column counts, row/col stretch factors, etc.
    • The number of rows and columns in the grid is independent of the number of columns you put in. You can have a single widget span multiple rows/cols, for example. You can even overlap widgets. Basically the rows and columns come first, they get sized, and then you lay on top the widgets spanning 1 or more rows and 1 or more columns.
    • When changing the stretch factor of a row/col, you need to update the children (they may move), but you don’t need to refit (the parent size didn’t change).
    • Standard lifecycle management for the layout.
    • Fitting: Ignore hidden children, tell teach child to do its own fitting, compute overall min/max sizing based on row/col spans, etc.
    • Updating: Solve for children size using the BoxEngine, and then computing starting position of each row, and starting position of each col, then iterating through the items to set the children absolute position. Overall it’s an O(n) algorithm!
    • Doesn’t yet have a GridPanel class. Great for someone new to the code to tackle!

7 June 2019

  • BoxPanel class:
    • This class, like many others, accepts a bag of options to the c’tor (i.e. a single object passed to the c’tor). This allows us to add more options in the future without breaking the API.
    • The options are passed to the super c’tor (which is Panel). The Panel options are superclass elements of the BoxPanel option elements.
    • Recall, CSS class names are prefixed with ‘p-’ and added to the DOM to each root element (wrt the Phosphor object)
    • Simple class (BoxPanel). It just gives you an easy way to have a BoxLayout without having to instantiate it and install it on your own widget.
  • SplitPanel class:
    • Very similar in style and goal to the BoxPanel, but different enough to be its own class (e.g. SplitPanel has to deal with the mouse).
    • Has an IRenderer to add virtual DOM nodes (e.g. the handle to drag the SplitPanel sections)
    • Proxies most attributes to the underlying SplitLayout class, so let’s talk about that next! (below)
  • SplitLayout class:
    • Takes options similar to the BoxLayout
      • The IRenderer is not optional!
      • (However, at the SplitPanel layer, it will give you a default IRender if you don’t specify one.)
    • Many getters/setters like BoxLayout
    • Has more behavior to deal with normalizing pixels (convert to %s), then converting back to pixels.
      • When you set sizes, “they don’t have to add to unity” since it will bet recomputed and normalized.
    • Logic for dealing with resizing is not in the mouse event callbacks. This allows you to change sizes programmatically (a simulated drag, sorta).
    • There’s a handled after every element (even the last one, but the last one is hidden and events on it are ignored). This allows the computation of sizes to be simpler.
    • BoxEngine does the math for sizes of children when handle is dragged. Tries hard to only change the two widgets on either side of the handle.
  • (back to) SplitPanel:
    • Uses a default renderer if not specified in the options (and even uses the same object as the default renderer, which is okay to do since it has no state).
    • The event listeners are defined here!
      • A single method handles multiple events (not usually how you register listeners, but makes sense in this case).
      • On mouse down, it’s important that we completely steal all mouse events for the whole document while it’s held down. We also globally override the cursor image while the mouse is down.

17 May 2019

  • growSizer function (from BoxEngine module):
    • Can’t do this behavior in CSS, has to be driven by JS. Smartly grows and shrinks widgets in a logical way.
      • Could maybe do something similar with CSS-grid, but Chris expects it will be more expensive with CSS regardless.
    • Demo here: http://phosphorjs.github.io/examples/datagrid/
      • If you have many widgets in a row, resizing one to the extreme will shrink its neighbors in a logical way.
  • BoxLayout
    • Options to c’tor for directions, alignments
    • Can get/set directions and alignments. Handles updating parent state upon being set.
    • Completely override attachWidget, moveWidget, detatchWidget. Needs to do different stuff than the base implementation.
    • This layout contains the first example of a widget that makes significant use of the widget lifecycle hooks. In particular, it avoids computation when the layout isn’t visible.
    • Logic of fit() and logic of update() are different. Reason: Resize needs to be more efficient (it happens every mouse move when dragging, for example).

03 May 2019

  • PanelLayout
    • Simplest concrete implementation of the Layout.
    • Intent is to lay out your widgets with CSS (as opposed to JS-powered absolute positioning).
      • Add children to parents which are laid out with CSS.
      • Fulfills messaging protocol (as any layout).
    • Commonly you will use the “Panel” widget (the PanelLayout counterpart).
    • Layouts dispose of their children (the layout owns the children, thus will dispose them).
    • The PanelLayout keeps an array of children (indexable).
      • Adding a new child reparents it, and move it to the index you wish it to be at.
    • If the PanelLayout has a parent, then we must actually do DOM manipulations when modifying children.
    • Init method goes further than the base init method as it needs to attach the children to the actual DOM.
    • All DOM manipulations are done in three protected methods: attachWidget(), moveWidget(), removeWidget().
      • Some subclasses of PanelLayout will reimplement these methods.
      • Overall goal is to keep DOM and Phosphor in-sync.
      • moveWidget():
        • In Chrome, moving is implemented as removing and inserting, so Phosphor just does this always (all browsers).
  • Generally the sister widget and layout are named “ThingPanel” and “ThingLayout”.
    • Exception is “Panel” (a widget) and “PanelLayout” (a layout).
  • You can’t change the layout on a widget after its been first set.
  • Panel:
    • Uses a PanelLayout.
    • If all you want to do is have a bunch of children laid out with CSS and throw it into a layout, use the Panel widget.
  • The boxengine module.
    • Doesn’t depend on anything. It’s just a math module.
    • Works in one-dimension using several BoxSizers.
    • Three methods:
      • calc: Docs are good description of what it does.

05 April 2019

  • Layout model
    • fitPolicy: all layouts in phosphor obey this
      • max sizes are not computed in many places. The fit policy used to have this, but it was taken away.
      • If we wanna computer the minimum size of the layout in a different way than just summing the minimum size of the children, then we can use the fit layout to toggle this behavior.

29 March 2019

  • Posting a message is useful, because it’s async and allows us to conflate messages. So multiple messages can be combined.
  • update will only be called once per event loop. layout your children.
    • The phosphor message loop, which runs once per animation frame.
  • fit:
    • Leaf nodes ignore this
    • Those that have children remeasure their children
    • goes up the tree
    • When a children changes their min/max sizes, it sends a fit request to it’s parent.
  • resize should be sent this whenever it may have changed it’s size. goes down the tree.
  • in CSS there is no concept of expanding a parent to fit it’s children. Here there is. It’s an inside out algorithm.
  • this layout algorithm doesn’t have a name but is used by QT and other desktop layouts
  • activate more generic than placing focus.
  • close default handler removes it from the hierarchy, it doesn’t dispose it or anything. You could intercept this with a message handler and throw up a dialogue box.
    • Clicking the x on the tab panel will trigger the close-request message to be sent.
  • show make it visible to the parent, might not be visible the page if parent is hidden
  • hide inverse. Sends messages so that other things can react.

22 March 2019

  • widgets
    • Some circular dependencies between layouts and widgets
    • This package is what most people associate with phosphor
    • Most other packages were built to be used by widgets
    • widget
      • Form a tree.
      • Disposal: When you dispose of one node, you dispose it’s children.
        • useful to remove widget from tab bar when it is disposed
      • Resize message: Notifies a widget if it has been resized.
        • It isn’t in the spec yet, but might at some point
        • You need this to accomplish layouts that can’t be done with CSS. So to do this, you need to know if the parent has been resized, so you can refit the children.
        • So to do this, either you need to either poll the dom or listen to the top level resize event.
          • top level resize event works, but doesn’t account for resize events that aren’t about window dragging.
        • The resize message propagates down in the widget hierarchy.
      • Every phosphor widget represents one node in the DOM
      • addClass: Can add class names, does this in a cross browser way
        • Convention is to call the classname same as JS class
      • node: It is public, for convenience. It has too much functionality to wrap. Although there are many convenience methods on the node. If a widget has a layout, modifying the node externally could cause some issues.
      • hidden and visible flags
        • When you call hide, it sets display of dom node to None. Still in the document, but hidden for CSS purposes
        • visible is not hidden, and none of ancestors are hidden.
        • Not related to CSS visibility or hidden flags
      • Can also get and set the parent of a widget.
        • set parent shouldn’t be used, besides in layout.
          • Except if you wanna remove it from the hierarchy you set it to null.
  • Phosphor 2.0: Should we drop phosphor’s iterators and just the standard? They are less efficient but are standard.
  • Common patterns
    • Have getters that return ISignal which return private Signal so third parties can’t emit signals

8 March 2019

  • Attendees
    • Saul Shanabrook / Quansight
    • Ivan Ogasawara / Quansight
    • Afshin Darian / Two Sigma
    • Jacob Houssian / Quansight
    • Katherine Oliphant / Quansight
  • dr``ag``drop:
    • Complete implementation of drag and drop functionality within a single browser window. Doesn’t allow dragging from desktop to window or vice versa. Why? Because that would require using browser APIs. These are limited because you cannot programmatically start and stop drag operations.
      • Why is that an issue? If you wanna have some behavior where drag behavior doesn’t start until user is dragging 15 px away from UI, you can’t do this in native drag operations. Only way to do it natively is with “draggable” attribute on DOM node, so you can’t do this.
      • As soon as you start handling mouse events yourself, you disable the drag and drop standards. So this required this package, which allows you to switch back and forth between native APIs and these custom ones.
      • Also built in supports only one type of data to store on drag event, strings. We can drag and drop custom JS objects, because it’s all in one page.
    • Followed the spec on the processing model
      • The IDragEvent is different but the process should be the same
    • supportedActions are set by creator of drag event
    • dropAction are set by consumer of drag event
    • mimeData is basically an ordered key value store of mimetypes to data.
    • source is provided by creator. Used to pass around some extra metadata.
    • Drag You are going to create this, when the user does something. For example, it is used in the tab bar, to see once user extends beyond some threshold for dragging.
      • most options are copied onto the event
      • start: clientX and clientY should be current x and y of users cursor. Only way to get this information is from event, so you have to grab this from the event.
        • between the time you call start and the time the promise resolves, phosphor takes control of mouse and keyboard. So you can assume all that will happen is user’s input will be used for dragging. You won’t get access to any of the keypress or mouse events.
    • Drag.overrideCursor: Allows you to overwrite the cursor the browser no matter where user is hovering.
    • Question: Can user press modifier while dragging to change intent?
      • Not right now. It isn’t implemented in native standard, but it could be added.
      • We would have to modify the event handling logic to check modifiers held on mouse move.
      • If this is needed we should open a feature request.
    • Question: Can we combine this with browser drag and drop?
      • If you wanna drag file from filebrowser to desktop, this isn’t possible.
  • vdom
    • React:
      • It had a weird licence before.
      • All we wanted was virutal dom and difffing, not the rest of it.
      • Back in the day, there was not typescript support for react.
      • I imagine for Phosphor 2.0 we would deprecate this and depend on React.
    • New users should probably use react for leaf nodes
  • widgets/title
    • Generic class with some object T.

1 March 2019

  • Attendees
    • Saul Shanabrook / Quansight
    • Ivan Ogasawara / Quansight
    • Afshin Darian / Two Sigma
    • Katherine Oliphant / Quansight
  • domutils
    • Similar to coreutils except these utilities have a dependency on the DOM.
    • elements: Related to DOM elements
      • boxSizing allows you get get padding/sizing of an element. This is important when doing explicit resizing. Instead of getting some sizes and then setting some, we get them all in one chunk. Mixing read/writes for styles isn’t performant.
      • sizeLimits: Converts between sizing
      • hitTest: See if element is over a certain position.
      • scrollIntoViewIfNeeded: Really helpful for manual scrolling. Used in the notebook to focus a particular cell. If that cell is not entirely visible, this function can be called with the canvas element that contains the relevant element and then the target element. It will scroll if needed.
    • platform:
      • simple platform detection, used for keybindings or bugs based on browser.
      • Most in phosphor is browser agnostic but some spots rely on it.
    • selector:
      • allows you to understand the specificity of a CSS selector
      • Used by the keyboard shortcut to choose the most specific selector and execute that keybinding.
      • This is specified by the CSS standard.
      • isValid: Allows you to compute whether a specifier is valid. Useful for proper error handler
      • matches: All browsers implement this, but some browsers dont implement it correctly, so this generalizes across browser. It also has a slow cache by querying document for all elements and check if select matches that element.
  • keyboard
    • Browsers can’t agree on key events so this module has to exist.
    • “Handling key events robustly in the browser is probably the most difficult thing to do”
    • There are three ways to determine what key the user presses. All but one on are deprecated.
    • We are currently used the keycode property, even though it is deprecated, because most browsers behave the same and implement it.
    • The key provided by the browser is the key code, like 105. However, this isn’t the key, because it depends on their keyboard layout. So 105 corresponds to different letters depending on locality.
    • So we need way to translate between these key codes to the characters the user pressed.
    • IKeyboardLayout: So we create an abstraction for different keyboard layouts. The meat here is keyForKeydownEvent which returns the string of the character the user pressed from the keyboard event.
      • Has a singleton keyboard layout for the application. Because it’s very unlikely that the user will have two keyboards plugged in. So there is just one globally.
      • On startup, JupyterLab could pick which layout is appropriate for the user and call setKeyboardLayout.
    • KeycodeLayout is an implementation
      • This uses the keycode, and takes in a mapping of key codes to character strings.
      • Could prompt user to press each key on keyboard and type in character.
      • We have uppercase characters because most keyboards print the uppercase versions. Also if we displayed lowercase, then people might be confused if SHIFT changed the character.
  • commands:
    • single CommandRegistryclass
    • This is an implementation of the "command” pattern
      • Where you have some action you wanna take where you don’t care about result but you wanna execute it.
    • We implement this as a registry. Why a registry, with string names, instead of independent objects? So that you can setup keybindings as JSON on disk, so that you can refer to this command in JSON.
    • There are two uses here: Author of command who registers this. Author of keybinding who registers keybinding for this command.
    • There are signals for when commands have changed, when a command is executed (for debugging), when a keybinding is changed.
    • command IDs should be unique. So you should namespace these with your module.
    • All the options are for building up UI around the command. Command registry does nothing with these options (except for isEnabled).
      • Usage: has example of the command in longer document
      • isEnabled: visual representation of whether we can execute it.
    • There is no remove command, because you should be responsible for removing it yourself. By using the dispose returns from addCommand.
    • notifyCommandChanged: Call this when you change the metadata attached to a command.
      • If I would go back, we would remove the ID as an arg, and we would just re-render totally whenever any changes a command. Because you often do a change that effects the metadata of a number of commands.
    • args: Has to be readonly JSON because they could be in a file, so they have to be JSON.
    • execute: Calls command and returns promise for result.
    • Questions:
      • Why allow a return value?
        • Often we return a promise to something that is available after the command is executed.
      • In jupyterlab we have many args that are just used for context generation. Basically is there a way to separate out args that are for metadata vs args for the execution of the commands
        • Maybe it would be helpful to separate the metadata generation from commandargs.
        • We could do this in command palette by passing different args to the different methods. The registry doesn’t care what args you pass it, it just passes those on.
  • General patterns
    • Every package has a index.ts that simply exports all public method from other files. So you always import from the package itself (coreutils), not a sub file (coreutils/elements)

15 February 2019

  • Attendees
    • Saul Shanabrook / Quansight
    • Afshin Darian / Two Sigma
    • Ivan Ogasawara / Quansight
    • Jacob Houssian / Quansight
    • Ryan Henning / Quansight
    • Katherine Oliphant / Quansight
  • properties
    • Copied from “C# attached property”
      • more verbose than in C# since it isn’t first class in JS
    • Type safe way to store data “on an object” you don’t own
      • i.e. you are dealing with a library and they hand you back a widget. You want to store extra data associated with that widget in some way. You can’t assign properties to it because it isn’t type safe. You can’t subclass because you are given it.
      • properties allow you keep this property around with a lifetime attached to the object by using a WeakMap, so that the properties are GC after the object is.
      • Don’t use this for your own objects. For that, it’s much more efficient to just put it in the class.
    • Usage:
      • Used in widgets package to store the title. It is created on demand, since not all widgets don’t need titles, so that is why it is a property not in the class itself. This way you don’t pay overhead of having this title object when it isn’t needed.
      • Various properties BoxLayout needs from widgets are set as properties. But these are hidden from the user by having getters/setters.
        • allows layout to be notified when properties are changed.
    • To access property you have to pass in owner/instance.
    • Options:
      • name: Just for show, has no effect
      • create: creates property when it is accessed the first time. Isn’t stored until it is first accessed.
      • changed : allows you to provide a callback when the attribute changes.
      • coerce: Allows us to transform the property when it is assigned
      • compare: Allows you to define comparison to check if property is different and should be updated.
    • Methods:
      • get/ set
      • clearData removes the data associated with the property.
        • Called in base widget class in dispose method
  • coreutils
    • Coreutils don’t have DOM dependency, whereas domutils do have a dependency.
    • json: type definitions for JSON. Also includes type guards and deep copy/equals.
    • mime: Mime data type: ordered key value map
    • PromiseDelegate: Allows you to create a promise and reject/resolve it later.
    • random: Getting crypto secure random values
    • uuid: create fast uuid4 values. If you don’t need globally unique ID, you can just increment IDs
    • token: Interfaces are erased at compile time. Sometimes we want to tell which interface something implements or tell between them. We can use a Token to maintain typing information, associated with a certain value.
      • Has a name associated with it and a structural property just to preserve the type, we don’t use it at runtime.
      • Used in JupyterLab for two things with the same name, but one is type and one is value. For when we are defining plugins.
      • Instead of using token we could use decorators and decorator metadata to assemble dependency injection framework. But since decorators are not finalized, they might change, we didn’t go this route. We also didn’t want someone to have to write typescript, which wouldn’t be possible with decorators since they aren’t part of the spec.
      • You could also just use unique strings, but then you could mispell it easily and wouldn’t get a compile time check.
  • Notes:
    • Private namespaces are defined at the bottom of the object
  • Questions:

08 February 2019

  • Attendees
    • Ryan Henning / Quansight
    • Saul Shanabrook / Quansight
    • Ivan Ogasawara / Quansight
    • Igor Derke / Quansight
    • Tony Fast / Quansight
    • Jason Grout / Bloomberg
    • Afshin Darian / Two Sigma
    • Jacob Houssian / Quansight
  • We have recording this week thanks to Luciano on WebEx.
  • signals:
    • “Contrapositive” to messaging.
      • One to many communication, vs many to one (messaging)
      • Signals are about
    • Signals are like JS events but not tied to dom hierarchy, so no “bubbling” up.
    • Any number can subscribe to a signal with a callback called a slot. (lifted from QT)
    • ISignal is immutable interface, allowing only connecting or disconnecting. So that classes can expose this, attach Signal as private attribute, so that external code can’t emit, only listen.
    • The first argument of the slot is the sender, the second is the value.
    • When you emit from a signal, this is analogous to sending a Message, i.e. all slots are invoked synchronously. But you don’t expect the emit method to throw, so any exceptions that are thrown will be caught and logged to the console.
    • There are some static methods on Signal to disconnect all receivers and senders of a certain object. We use the clearData in the dispose method of the widget to remove all its connections.
    • All connections are stored with weakmaps, so if your object falls out of scope (is garbage collected) it will be cleared.
    • Similar to event-emitter package in Node.
    • What about composing signals?
      • Observables are more about push based data, where as signals are more like events. Composing signals (mapping/filtering, etc) in a functional way isn’t how we think of signals. Instead we could create a new signal and subscribe to an old one and emit on the new one. We shouldn’t use the functional patterns because they don’t map well with the garbage collection with regards to senders.
  • Disposable
    • Track resources and handle cleanups throughout application.
    • in C# this is called the Disposable pattern. Object has dispose method. Guarantee is that every call after the first is a no-op. Also has a way to retrieve whether it has been disposed.
    • Has disposable data structures as well, that dispose their contents on disposal.
    • What if there was a convenience class that has logic to add disposed signal?
      • Chris isn’t opposed to adding additional interface to add signal to interface.
      • He is opposed to making a convenience class, because then you feel encouraged to inherit this base class, and requires having all this logic embedded in your class. Don’t want to encourage this type of behavior.
      • DisposableSet don’t have a disposed signal because of memory overhead.

01 February 2019

  • Attendees
    • Saul Shanabrook / Quansight
    • Ryan Henning / Quansight
    • Chris Colbert
    • Katherine Oliphant / Quansight
    • Igor Derke / Quansight
    • Afshin Darian / Two Sigma
    • Jacob Houssian / Quansight
    • Jason Grout / Bloomberg
    • Vidar Fauske / Simula Research Laboratory
    • Ivan Ogasawara / Quansight
    • Luciano / IBM
  • Rich Context is some new work for JupyterLab
    • Datasets and data converters
    • Commenting and annotation: click on file/cell and add comments
    • Metadata: Have some metadata associated with a dataset
  • Possible agenda:
    • Walk through particular packages in Phosphor and walk through them. Starting from bottom of the stack.
      • Hear about alternative ways these could be implemented, to understand where they came from.
    • Those already working on jupyterlab know least about datastore and datagrid packages, the most recent work.
    • Datastore is work we need to hand off and contribute to most actively.
    • How to add comments to Data Grid?
  • Different needs:
    • Getting familiar with Phosphor in general
    • Getting familiar with Datastore in particular
  • Next time: maybe record meeting? Lots of information.
  • Useful artifact could be design document describing phosphor at a high level.
    • Phospher isn’t one thing, it’s a collection of several packages (./packages)
      • Each one can stand by itself, although there is some interconnections
    • No external dependencies (0)
    • algorithm and collections are at the bottom.
      • algorithm: JS doesn’t come with many core data types. (similar to C++ std algorithm)
        • iterators came before ES6 iterators, and are strictly more powerful.
          • You can clone phosphor iterators which you can’t do with ES6 iterators.
          • Calling next returns value or undefined, not valid done pair, which is faster.
          • There is some debate on whether to deprecate these and move to ES6 iterator. This could be a 2.0 change, because it is breaking.
        • They all work on iterators, where as standard JS algorithms all work on arrays, nott iterators (map, etc).
        • Also supports more algorithms than JS like topologicalSort. Yes you could depend on NPM package for this, but then you end in dependency hell
        • Like lodash or jquery
      • collections
        • small package, but could grow in future with more data structures
        • implement phosphor iterator protocol
        • Linked list
          • used for queue
          • better algorithmic complexity for pushing/popping than JS array
        • b plus tree
          • ordered key value map
          • useful for ordered collection of things with logarithmic time for all operations
          • used in the datastore
    • messaging
      • Implementation of QT’s event pattern, without event bubbling
      • didn’t call it events to differentiate from JS events.
        • In JS, multiple consumers can subscribe to one event emitter. One to many.
        • Phosphor messaging pattern is many to one. Many message creators and one listener
      • IMessageHandler: interface that allows you to opt in to handling messages of certain types, but it doesn’t have to (processMessage should not throw an error on unrecognized messages)
        • Many objects handed to me, but I need one to be implement some behavior.
          • I could have a method on them that can implement some named method.
            • i.e. a protocol
          • Instead can message passing algorithm. You have to implement a processMessage method and you get a string of the message type.
            • then you only have to implement one method instead of a lot of them
          • processMessage essentially is a level of indirection for implementing functionality, with its own namespace (so you don’t have to mess up the method namespace)
          • Used heavily inside of widgets, for attachment notifications, resize messages, paint notifications, layout messages, close message
        • Can “post” a message ( “deferred”/ asynchronous processing) or “send” a message (synchronous processing). “post” vs “send” naming familiar from win32 api?
          • Deferred message can be “conflated”. Subclass of message ConflatableMessage that we can use to combine a bunch of messages as one, when we dispatch them.
          • If we have multiple conflatable messages of the same type in the event queue then we combine (conflate) them and replace the one in the front of the queue.
          • i.e PaintRequest implements conflate method, by expanding “dirty” bands to contain scope of both of them.
          • Earliest message in queue gets notified about later messages in queue and has an option to conflate it (i.e., update itself from the contents of the later message, and return true to remove the later message from the queue).
          • conflatable messages should be able to be reordered with respect to other types of messages and have no effect. because they can be conflated and merged.
      • IMessageHook allows us to spy on messages that are sent or filter them
        • if you are a parent widget and want to know if child is received, you can install a message hook on the child’s resize event, to intercept it and return false to filter it out.
        • This happens in a close-request method on widgets, any widget can be prevented from being closed. i.e. could have a popup to stop it from being closed.
        • Also used in ipywidgets to allow resize events to be handled by it’s own handler.
      • MessageLoop is a namespace that takes care of all this. Global, only one at a time.
        • sendMessage runs all hooks and sends (?) message if all hooks return true
        • installMessageHook / removeMessageHook add remove message hooks for handler
        • clearData removes all message hooks and all messages in the queue for a certain handler.
          • called in destroy method on a widget to clear all data from message queue.
        • Also functions for getting/setting exception handler.
          • Whenever you send or post a message, the handler might throw an exception. From an API perspective you don’t wanna worry about this. So message loop guards execution in try…catch and logs error to console by default.
          • can set a new behavior.
          • mostly for debugging, handlers shouldn’t throw exceptions.
  • Conclusions
    • Before next meeting figure out how to record
      • Luciano volunteered IBMs chat software
      • Jason volunteered the jupyter zoom channel
    • Jason suggested that we take these notes and compile them to design documents and have chris review them to create documentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment