Skip to content

Mythic

Mythic

The Mythic group contains an abstraction for writing individual pieces of business logic into packages. These packages are Myths and contain definitions for Redux actions, reducers, and epics.

Table of contents


/mythic-configuration

The @nteract/mythic-configuration package is still in development. For more information, reach out to the community on GitHub.

Examples of /mythic-configuration

Initialize the package by including the configuration package. Memory saves the configuration by default.

Example:

To use a config file, dispatch a setConfigFile action following the code below.

import { configuration } from "@nteract/mythic-configuration";
import { makeConfigureStore } from "@nteract/myths";

export const configureStore = makeConfigureStore({
  packages: [configuration],
});

store.dispatch(setConfigFile("/etc/app.conf"));

The package saves any configuration options to that file and tracks it. Any additional options load as the file changes.

Configuration options are available after initialization.

Example:

export const {
  selector: tabSize,
  action: setTabSize,
} = defineConfigOption({
  label: "Tab Size",
  key: "codeMirror.tabSize",
  values: [
    {label: "2 Spaces", value: 2},
    {label: "3 Spaces", value: 3},
    {label: "4 Spaces", value: 4},
  ],
  defaultValue: 4,
});

const currentValue = tabSize(store.getState());

store.dispatch(setTabSize(2));

To get an object from all config options with a common prefix, use createConfigCollection.

Example:

const codeMirrorConfig = createConfigCollection({
  key: "codeMirror",
});

The codeMirrorConfig() provides an above option {tabSize: 4}, with proper default values.

All options are available with the code below.

import { allConfigOptions } from "@nteract/mythic-configuration";

const options = allConfigOptions();
const optionsWithCurrentValues = allConfigOptions(store.getState());

In order to change the key of a config option, deprecate the old key with the following code. This changes the old key to the new key, unless the new key already has a value.

Example:

createDeprecatedConfigOption({
  key: "cursorBlinkRate",
  changeTo: (value: number) => ({
    "codeMirror.cursorBlinkRate": value,
  }),
});

API for /mythic-configuration

The API for the @nteract/mythic-configuration package is still in development. For more information, reach out to the community on GitHub.

Example:

import { RootState } from "@nteract/myths";
import { ConfigurationState, setConfigAtKey } from "@nteract/mythic-configuration";

export interface ConfigurationOptionDefinition<TYPE = any> {
  label: string;
  key: string;
  defaultValue: TYPE;
  valuesFrom?: string;
  values?: Array<{
    label: string;
    value: TYPE;
  }>;
}

export interface ConfigurationOption<TYPE = any>
  extends ConfigurationOptionDefinition<TYPE> {

  value?: TYPE;
  selector: (state: HasPrivateConfigurationState) => TYPE;
  action: (value: TYPE) => typeof setConfigAtKey.action;
}

export type HasPrivateConfigurationState =
  RootState<"configuration", ConfigurationState>;

/mythic-multiselect

The @nteract/mythic-multiselect package implements a simple method of keeping track of multiple selected cells using the myths framework.

Examples of /mythic-multiselect

Initialize the package by including the notifications package in your store and rendering the <NotificationsRoot/>:

Example:

import {
  multiselect,
  selectCell,
  unselectCell,
  clearSelectedCells,
} from "@nteract/mythic-multiselect";

store.dispatch(
  selectCell({
    contentRef: "content",
    id: "cellID",
  })
);

API for /mythic-multiselect

This content is still in development. For more information, reach out to the community on GitHub.

/mythic-notifications

The @nteract/mythic-notifications package implements a notification system based on blueprintjs, using the myths framework.

Examples of /mythic-notifications

Initialize the package by including the notifications package in your store and rendering the <NotificationsRoot/>:

Example:

import { notifications, NotificationRoot } from "@nteract/mythic-notifications";
import { makeConfigureStore } from "@nteract/myths";

export const configureStore = makeConfigureStore({
  packages: [notifications],
});

export const App = () =>
    <>
      {/* ... */}
      <NotificationRoot darkTheme={false} />
    </>

Then dispatch actions made by sendNotification.create:

import { sendNotification } from "@nteract/mythic-notifications";

store.dispatch(sendNotification.create({
  title: "Hello World!",
  message: <em>Hi out there!</em>,
  level: "info",
}));

API for /mythic-notifications

import { IconName } from "@blueprintjs/core";

export interface NotificationMessage {
  key?: string;
  icon?: IconName;
  title?: string;
  message: string | JSX.Element;
  level: "error" | "warning" | "info" | "success" | "in-progress";
  action?: {
    icon?: IconName;
    label: string;
    callback: () => void;
  };
}

/mythic-windowing

The @nteract/mythic-windowing package implements a windowing system based on electron, using the myths framework.

Examples of /mythic-windowing

Initialize the package by including the windowing package in your store:

Example:

import { windowing, setWindowingBackend, electronBackend } from "@nteract/mythic-windowing";
import { makeConfigureStore } from "@nteract/myths";

export const configureStore = makeConfigureStore({
  packages: [windowing],
});

store.dispatch(setWindowingBackend.create(electronBackend));

const electronReady$ = new Observable((observer) => {
  (app as any).on("ready", launchInfo => observer.next(launchInfo));
});

electronReady$
  .subscribe(
    () => store.dispatch(
      showWindow.create({
        id: "splash",
        kind: "splash",
        width: 565,
        height: 233,
        path: join(__dirname, "..", "static", "splash.html"),
      })
    ),
    (err) => console.error(err),
    () => store.dispatch(
      closeWindow.create("splash")
    ),
  );

API for /mythic-windowing

This content is still in development. For more information, reach out to the community on GitHub.

/myths

The myths framework allows for integrating sets of closely related actions, reducers and epics. Myths allow close relationships where DRY and dependencies are minimized. Myths provide structured way to avoid boilerplate code.

Myths build on top of the Redux and RxJS libraries.

Redux helps to maintain the application state. In Redux, actions and reducers provide predictable state management. The state changes only when dispatching an action to a reducer.

In Redux-Observable, an epic is a function that takes in a stream of actions and returns a stream of actions.

Examples of /myths

This content is still in development. For more information, reach out to the community on GitHub.

MythicPackage

Create a MythicPackage with a name, a type for its private state, and the initial state.

The example below creates a MythicPackage named "iCanAdd" which uses the number type for its private state sum and an initial state of sum as 0:

Example:

export const iCanAdd = createMythicPackage("iCanAdd")<
  {
    sum: number;
  }
>({
  initialState: {
    sum: 0,
  },
});

Myth

Next, use the MythicPackage to create a Myth with a name, a type for its payload, and optionally a reducer operating on its package's private state.

In example below, the MythicPackage named iCanAdd creates a Myth named "addToSum".

Example:

export const addToSum =
  iCanAdd.createMyth("addToSum")<number>({
    reduce: (state, action) =>
      state.set("sum", state.get("sum") + action.payload),
  });

A package can have any number of myths.

Action

To create an action based on a myth, use its create function and dispatch this action normally.

Example:

store.dispatch(addToSum.create(8));

Store

A set of mythic packages yields a store. This store has all the appropriate reducers and epics in place.

Example:

type NonPrivateState = { foo: string };
const configureStore = makeConfigureStore<NonPrivateState>()({
  packages: [
    iCanAdd,
  ],
});
export const store = configureStore({ foo: "bar" });

Definition of epics

Define epics using two different shorthand methods.

Example:

export const addToSum =
  iCanAdd.createMyth("addToSum")<number>({

    reduce: (state, action) =>
      state.set("sum", state.get("sum") + action.payload),

    thenDispatch: [
      (action, state) =>
        state.get("sum") - action.payload < 100 && 100 <= state.get("sum")
          ? of(sendNotification.create({message: "Just passed 100!"}))
          : EMPTY,
    ],

    andAlso: [
      {
        // Halve the sum every time an error action happens
        when: action => action.error ?? false,
        dispatch: (action, state, addToSum_) =>
          of(addToSum_.create(-state.get("sum") / 2)),
      },
    ],

  });

The first method uses thenDispatch: [] to define actions. These dispatch at the same time as defined type actions. The second method uses andAlso: [] to generate actions based on a custom predicate.

Defining the type means the type is not available for reference yet. The type passes as the third argument to the dispatch function.

Testing

To test the actions of a mythic package, use the testMarbles(...) method.

NOTE: This only tests the epics without evaluating reducers.