jupyterpack 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## Features
9
9
 
10
- - **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports Dash, Streamlit and Shiny for Python. You can also use `jupyterpack` to serve any `asgi` or `wsgi` Python web application.
10
+ - **Python Web Apps**: Serve Python web applications directly in the browser using JupyterLite's in-browser Python kernel. `jupyterpack` currently supports Dash, Streamlit and Shiny for Python. You can also use `jupyterpack` to serve any `Starlette` or `Tornado` application.
11
11
  .
12
12
  - **JavaScript Web Apps**: Bundle and serve JavaScript web applications using in-browser bundlers.
13
13
 
@@ -1,3 +1,4 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  import { IJupyterpackDocTracker } from '../type';
3
3
  export declare const spkPlugin: JupyterFrontEndPlugin<IJupyterpackDocTracker>;
4
+ export declare const launcherPlugin: JupyterFrontEndPlugin<void>;
@@ -1,8 +1,11 @@
1
- import { JupyterPackWidgetFactory } from './widgetFactory';
2
- import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
1
+ import { JupyterPackFramework } from './../type';
3
2
  import { WidgetTracker } from '@jupyterlab/apputils';
4
- import { logoIcon } from '../tools';
3
+ import { ILauncher } from '@jupyterlab/launcher';
4
+ import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
5
+ import { dashIcon, logoIcon, shinyIcon, streamlitIcon } from '../tools';
5
6
  import { addCommands } from './commands';
7
+ import { JupyterPackWidgetFactory } from './widgetFactory';
8
+ import { generateAppFiles } from './templates';
6
9
  const FACTORY = 'jupyterpack';
7
10
  const CONTENT_TYPE = 'jupyterpack';
8
11
  export const spkPlugin = {
@@ -47,3 +50,113 @@ export const spkPlugin = {
47
50
  return tracker;
48
51
  }
49
52
  };
53
+ export const launcherPlugin = {
54
+ id: 'jupyterpack:spklauncher',
55
+ optional: [ILauncher],
56
+ autoStart: true,
57
+ provides: IJupyterpackDocTrackerToken,
58
+ activate: (app, launcher) => {
59
+ if (!launcher) {
60
+ return;
61
+ }
62
+ const { commands } = app;
63
+ const commandId = 'jupyterpack:create-new-file';
64
+ const dashCommandId = 'jupyterpack:create-dash-app';
65
+ const streamlitCommandId = 'jupyterpack:create-streamlit-app';
66
+ const shinyCommandId = 'jupyterpack:create-shiny-app';
67
+ commands.addCommand(commandId, {
68
+ label: 'New SPK File',
69
+ icon: logoIcon,
70
+ caption: 'Create a new SPK fike',
71
+ execute: async (args) => {
72
+ const cwd = args['cwd'];
73
+ const contentsManager = app.serviceManager.contents;
74
+ let model = await contentsManager.newUntitled({
75
+ path: cwd,
76
+ type: 'file',
77
+ ext: '.spk'
78
+ });
79
+ const spkContent = `
80
+ {
81
+ "name": "${model.name}",
82
+ "entry": "",
83
+ "framework": "",
84
+ "rootUrl": "/",
85
+ "metadata": {
86
+ "autoreload": true
87
+ },
88
+ "dependencies": {
89
+ "mamba": [],
90
+ "pip": []
91
+ }
92
+ }
93
+ `;
94
+ model = await contentsManager.save(model.path, {
95
+ ...model,
96
+ format: 'text',
97
+ size: undefined,
98
+ content: spkContent
99
+ });
100
+ }
101
+ });
102
+ commands.addCommand(dashCommandId, {
103
+ label: 'Dash App',
104
+ icon: dashIcon,
105
+ caption: 'Create a new Dash Application',
106
+ execute: async (args) => {
107
+ const cwd = args['cwd'];
108
+ await generateAppFiles({
109
+ contentsManager: app.serviceManager.contents,
110
+ cwd,
111
+ framework: JupyterPackFramework.DASH
112
+ });
113
+ }
114
+ });
115
+ commands.addCommand(streamlitCommandId, {
116
+ label: 'Streamlit App',
117
+ icon: streamlitIcon,
118
+ caption: 'Create a new Streamlit Application',
119
+ execute: async (args) => {
120
+ const cwd = args['cwd'];
121
+ await generateAppFiles({
122
+ contentsManager: app.serviceManager.contents,
123
+ cwd,
124
+ framework: JupyterPackFramework.STREAMLIT
125
+ });
126
+ }
127
+ });
128
+ commands.addCommand(shinyCommandId, {
129
+ label: 'Shiny App',
130
+ icon: shinyIcon,
131
+ caption: 'Create a new Shiny Application',
132
+ execute: async (args) => {
133
+ const cwd = args['cwd'];
134
+ await generateAppFiles({
135
+ contentsManager: app.serviceManager.contents,
136
+ cwd,
137
+ framework: JupyterPackFramework.SHINY
138
+ });
139
+ }
140
+ });
141
+ launcher.add({
142
+ command: commandId,
143
+ category: 'JupyterPack',
144
+ rank: 1
145
+ });
146
+ launcher.add({
147
+ command: dashCommandId,
148
+ category: 'JupyterPack',
149
+ rank: 2
150
+ });
151
+ launcher.add({
152
+ command: streamlitCommandId,
153
+ category: 'JupyterPack',
154
+ rank: 3
155
+ });
156
+ launcher.add({
157
+ command: shinyCommandId,
158
+ category: 'JupyterPack',
159
+ rank: 4
160
+ });
161
+ }
162
+ };
@@ -0,0 +1 @@
1
+ export declare const DASH_APP = "\n# This example is taken from the Dash official documentation (https://dash.plotly.com/layout)\n\nimport plotly.express as px\nimport pandas as pd\nfrom dash import Dash, html, dcc\napp = Dash()\n\n# assume you have a \"long-form\" data frame\n# see https://plotly.com/python/px-arguments/ for more options\ndf = pd.DataFrame({\n \"Fruit\": [\"Apples\", \"Oranges\", \"Bananas\", \"Apples\", \"Oranges\", \"Bananas\"],\n \"Amount\": [4, 1, 2, 2, 4, 5],\n \"City\": [\"SF\", \"SF\", \"SF\", \"Montreal\", \"Montreal\", \"Montreal\"]\n})\n\nfig = px.bar(df, x=\"Fruit\", y=\"Amount\", color=\"City\", barmode=\"group\")\n\napp.layout = html.Div(children=[\n html.H1(children='Hello Dash'),\n\n html.Div(children='''\n Dash: A web application framework for your data.\n '''),\n\n dcc.Graph(\n id='example-graph',\n figure=fig\n )\n])";
@@ -0,0 +1,30 @@
1
+ export const DASH_APP = `
2
+ # This example is taken from the Dash official documentation (https://dash.plotly.com/layout)
3
+
4
+ import plotly.express as px
5
+ import pandas as pd
6
+ from dash import Dash, html, dcc
7
+ app = Dash()
8
+
9
+ # assume you have a "long-form" data frame
10
+ # see https://plotly.com/python/px-arguments/ for more options
11
+ df = pd.DataFrame({
12
+ "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
13
+ "Amount": [4, 1, 2, 2, 4, 5],
14
+ "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
15
+ })
16
+
17
+ fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
18
+
19
+ app.layout = html.Div(children=[
20
+ html.H1(children='Hello Dash'),
21
+
22
+ html.Div(children='''
23
+ Dash: A web application framework for your data.
24
+ '''),
25
+
26
+ dcc.Graph(
27
+ id='example-graph',
28
+ figure=fig
29
+ )
30
+ ])`;
@@ -0,0 +1,7 @@
1
+ import { Contents } from '@jupyterlab/services';
2
+ import { JupyterPackFramework } from '../../type';
3
+ export declare function generateAppFiles(options: {
4
+ contentsManager: Contents.IManager;
5
+ framework: JupyterPackFramework;
6
+ cwd: string;
7
+ }): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { JupyterPackFramework } from '../../type';
2
+ import { DASH_APP } from './dash';
3
+ import { STREAMLIT_APP } from './streamlit';
4
+ import { SHINY_APP } from './shiny';
5
+ export async function generateAppFiles(options) {
6
+ const { contentsManager, framework, cwd } = options;
7
+ const pyModel = await contentsManager.newUntitled({
8
+ path: cwd,
9
+ type: 'file',
10
+ ext: '.py'
11
+ });
12
+ let appContent = '';
13
+ switch (framework) {
14
+ case JupyterPackFramework.DASH: {
15
+ appContent = DASH_APP;
16
+ break;
17
+ }
18
+ case JupyterPackFramework.STREAMLIT: {
19
+ appContent = STREAMLIT_APP;
20
+ break;
21
+ }
22
+ case JupyterPackFramework.SHINY: {
23
+ appContent = SHINY_APP;
24
+ break;
25
+ }
26
+ default:
27
+ break;
28
+ }
29
+ await contentsManager.save(pyModel.path, {
30
+ ...pyModel,
31
+ format: 'text',
32
+ size: undefined,
33
+ content: appContent
34
+ });
35
+ let model = await contentsManager.newUntitled({
36
+ path: cwd,
37
+ type: 'file',
38
+ ext: '.spk'
39
+ });
40
+ const spkContent = `
41
+ {
42
+ "name": "${model.name}",
43
+ "entry": "${pyModel.name}",
44
+ "framework": "${framework}",
45
+ "dependencies": {
46
+ "mamba": []
47
+ }
48
+ }
49
+ `;
50
+ model = await contentsManager.save(model.path, {
51
+ ...model,
52
+ format: 'text',
53
+ size: undefined,
54
+ content: spkContent
55
+ });
56
+ }
@@ -0,0 +1 @@
1
+ export declare const SHINY_APP = "\n# This example is taken from the shinylive official playground (https://shinylive.io/py/examples/#modules)\n\nfrom shiny import App, module, reactive, render, ui\n\n\n# ============================================================\n# Counter module\n# ============================================================\n@module.ui\ndef counter_ui(label: str = \"Increment counter\") -> ui.TagChild:\n return ui.card(\n ui.h2(\"This is \" + label),\n ui.input_action_button(id=\"button\", label=label),\n ui.output_code(id=\"out\"),\n )\n\n\n@module.server\ndef counter_server(input, output, session, starting_value: int = 0):\n count: reactive.value[int] = reactive.value(starting_value)\n\n @reactive.effect\n @reactive.event(input.button)\n def _():\n count.set(count() + 1)\n\n @render.code\n def out() -> str:\n return f\"Click count is {count()}\"\n\n\n# =============================================================================\n# App that uses module\n# =============================================================================\napp_ui = ui.page_fluid(\n counter_ui(\"counter1\", \"Counter 1\"),\n counter_ui(\"counter2\", \"Counter 2\"),\n)\n\n\ndef server(input, output, session):\n counter_server(\"counter1\")\n counter_server(\"counter2\")\n\n\napp = App(app_ui, server)\n\n";
@@ -0,0 +1,49 @@
1
+ export const SHINY_APP = `
2
+ # This example is taken from the shinylive official playground (https://shinylive.io/py/examples/#modules)
3
+
4
+ from shiny import App, module, reactive, render, ui
5
+
6
+
7
+ # ============================================================
8
+ # Counter module
9
+ # ============================================================
10
+ @module.ui
11
+ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
12
+ return ui.card(
13
+ ui.h2("This is " + label),
14
+ ui.input_action_button(id="button", label=label),
15
+ ui.output_code(id="out"),
16
+ )
17
+
18
+
19
+ @module.server
20
+ def counter_server(input, output, session, starting_value: int = 0):
21
+ count: reactive.value[int] = reactive.value(starting_value)
22
+
23
+ @reactive.effect
24
+ @reactive.event(input.button)
25
+ def _():
26
+ count.set(count() + 1)
27
+
28
+ @render.code
29
+ def out() -> str:
30
+ return f"Click count is {count()}"
31
+
32
+
33
+ # =============================================================================
34
+ # App that uses module
35
+ # =============================================================================
36
+ app_ui = ui.page_fluid(
37
+ counter_ui("counter1", "Counter 1"),
38
+ counter_ui("counter2", "Counter 2"),
39
+ )
40
+
41
+
42
+ def server(input, output, session):
43
+ counter_server("counter1")
44
+ counter_server("counter2")
45
+
46
+
47
+ app = App(app_ui, server)
48
+
49
+ `;
@@ -0,0 +1 @@
1
+ export declare const STREAMLIT_APP = "\n# This example is taken from the streamlit official playground (https://streamlit.io/playground?example=charts)\n\nimport streamlit as st\nimport pandas as pd\nimport numpy as np\n\nst.write(\n \"Streamlit supports a wide range of data visualizations, including [Plotly, Altair, and Bokeh charts](https://docs.streamlit.io/develop/api-reference/charts). \uD83D\uDCCA And with over 20 input widgets, you can easily make your data interactive!\"\n)\n\nall_users = [\"Alice\", \"Bob\", \"Charly\"]\nwith st.container(border=True):\n users = st.multiselect(\"Users\", all_users, default=all_users)\n rolling_average = st.toggle(\"Rolling average\")\n\nnp.random.seed(42)\ndata = pd.DataFrame(np.random.randn(20, len(users)), columns=users)\nif rolling_average:\n data = data.rolling(7).mean().dropna()\n\ntab1, tab2 = st.tabs([\"Chart\", \"Dataframe\"])\ntab1.line_chart(data, height=250)\ntab2.dataframe(data, height=250, width=True)\n\n";
@@ -0,0 +1,26 @@
1
+ export const STREAMLIT_APP = `
2
+ # This example is taken from the streamlit official playground (https://streamlit.io/playground?example=charts)
3
+
4
+ import streamlit as st
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ st.write(
9
+ "Streamlit supports a wide range of data visualizations, including [Plotly, Altair, and Bokeh charts](https://docs.streamlit.io/develop/api-reference/charts). 📊 And with over 20 input widgets, you can easily make your data interactive!"
10
+ )
11
+
12
+ all_users = ["Alice", "Bob", "Charly"]
13
+ with st.container(border=True):
14
+ users = st.multiselect("Users", all_users, default=all_users)
15
+ rolling_average = st.toggle("Rolling average")
16
+
17
+ np.random.seed(42)
18
+ data = pd.DataFrame(np.random.randn(20, len(users)), columns=users)
19
+ if rolling_average:
20
+ data = data.rolling(7).mean().dropna()
21
+
22
+ tab1, tab2 = st.tabs(["Chart", "Dataframe"])
23
+ tab1.line_chart(data, height=250)
24
+ tab2.dataframe(data, height=250, width=True)
25
+
26
+ `;
package/lib/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IJupyterpackDocTracker> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IConnectionManager>)[];
1
+ declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IJupyterpackDocTracker> | import("@jupyterlab/application").JupyterFrontEndPlugin<void> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./type").IConnectionManager>)[];
2
2
  export default _default;
package/lib/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { spkPlugin } from './document/plugin';
1
+ import { launcherPlugin, spkPlugin } from './document/plugin';
2
2
  import { swPlugin } from './swConnection';
3
- export default [swPlugin, spkPlugin];
3
+ export default [swPlugin, spkPlugin, launcherPlugin];
package/lib/tools.d.ts CHANGED
@@ -3,6 +3,9 @@ export declare const IS_LITE: boolean;
3
3
  export declare const logoIcon: LabIcon;
4
4
  export declare const autoReloadIcon: LabIcon;
5
5
  export declare const linkIcon: LabIcon;
6
+ export declare const dashIcon: LabIcon;
7
+ export declare const streamlitIcon: LabIcon;
8
+ export declare const shinyIcon: LabIcon;
6
9
  export declare function removePrefix(path: string, prefix: string): string;
7
10
  export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
8
11
  export declare function base64ToArrayBuffer(base64: string): Uint8Array;
package/lib/tools.js CHANGED
@@ -1,6 +1,9 @@
1
1
  import logoStr from '../style/icons/box.svg';
2
2
  import autoReloadStr from '../style/icons/autoreload.svg';
3
3
  import linkStr from '../style/icons/externallink.svg';
4
+ import dashStr from '../style/icons/Plotly-Logo-Black.svg';
5
+ import streamlitStr from '../style/icons/streamlit-logo-primary.svg';
6
+ import shinyStr from '../style/icons/shiny-for-python.svg';
4
7
  import { LabIcon } from '@jupyterlab/ui-components';
5
8
  export const IS_LITE = !!document.getElementById('jupyter-lite-main');
6
9
  export const logoIcon = new LabIcon({
@@ -15,6 +18,18 @@ export const linkIcon = new LabIcon({
15
18
  name: 'jupyterpack:externalLink',
16
19
  svgstr: linkStr
17
20
  });
21
+ export const dashIcon = new LabIcon({
22
+ name: 'jupyterpack:dashBlack',
23
+ svgstr: dashStr
24
+ });
25
+ export const streamlitIcon = new LabIcon({
26
+ name: 'jupyterpack:streamlitBlack',
27
+ svgstr: streamlitStr
28
+ });
29
+ export const shinyIcon = new LabIcon({
30
+ name: 'jupyterpack:shinyLogo',
31
+ svgstr: shinyStr
32
+ });
18
33
  export function removePrefix(path, prefix) {
19
34
  if (path.startsWith(prefix)) {
20
35
  return path.slice(prefix.length);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterpack",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "A JupyterLab extension for serving web app.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -61,6 +61,7 @@
61
61
  "dependencies": {
62
62
  "@codesandbox/sandpack-client": "^2.19.8",
63
63
  "@jupyterlab/application": "^4.0.0",
64
+ "@jupyterlab/launcher": "^4.0.0",
64
65
  "comlink": "^4.4.2",
65
66
  "strip-ansi": "^7.1.2"
66
67
  },
@@ -1,15 +1,18 @@
1
+ import { JupyterPackFramework } from './../type';
1
2
  import {
2
3
  JupyterFrontEnd,
3
4
  JupyterFrontEndPlugin
4
5
  } from '@jupyterlab/application';
5
-
6
- import { JupyterPackWidgetFactory } from './widgetFactory';
7
- import { IConnectionManager, IJupyterpackDocTracker } from '../type';
8
- import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
9
6
  import { WidgetTracker } from '@jupyterlab/apputils';
10
7
  import { DocumentWidget } from '@jupyterlab/docregistry';
11
- import { logoIcon } from '../tools';
8
+ import { ILauncher } from '@jupyterlab/launcher';
9
+
10
+ import { IConnectionManagerToken, IJupyterpackDocTrackerToken } from '../token';
11
+ import { dashIcon, logoIcon, shinyIcon, streamlitIcon } from '../tools';
12
+ import { IConnectionManager, IJupyterpackDocTracker } from '../type';
12
13
  import { addCommands } from './commands';
14
+ import { JupyterPackWidgetFactory } from './widgetFactory';
15
+ import { generateAppFiles } from './templates';
13
16
 
14
17
  const FACTORY = 'jupyterpack';
15
18
  const CONTENT_TYPE = 'jupyterpack';
@@ -63,3 +66,119 @@ export const spkPlugin: JupyterFrontEndPlugin<IJupyterpackDocTracker> = {
63
66
  return tracker;
64
67
  }
65
68
  };
69
+
70
+ export const launcherPlugin: JupyterFrontEndPlugin<void> = {
71
+ id: 'jupyterpack:spklauncher',
72
+ optional: [ILauncher],
73
+ autoStart: true,
74
+ provides: IJupyterpackDocTrackerToken,
75
+ activate: (app: JupyterFrontEnd, launcher: ILauncher | null): void => {
76
+ if (!launcher) {
77
+ return;
78
+ }
79
+ const { commands } = app;
80
+ const commandId = 'jupyterpack:create-new-file';
81
+ const dashCommandId = 'jupyterpack:create-dash-app';
82
+ const streamlitCommandId = 'jupyterpack:create-streamlit-app';
83
+ const shinyCommandId = 'jupyterpack:create-shiny-app';
84
+
85
+ commands.addCommand(commandId, {
86
+ label: 'New SPK File',
87
+ icon: logoIcon,
88
+ caption: 'Create a new SPK fike',
89
+ execute: async args => {
90
+ const cwd = args['cwd'] as string;
91
+ const contentsManager = app.serviceManager.contents;
92
+ let model = await contentsManager.newUntitled({
93
+ path: cwd,
94
+ type: 'file',
95
+ ext: '.spk'
96
+ });
97
+ const spkContent = `
98
+ {
99
+ "name": "${model.name}",
100
+ "entry": "",
101
+ "framework": "",
102
+ "rootUrl": "/",
103
+ "metadata": {
104
+ "autoreload": true
105
+ },
106
+ "dependencies": {
107
+ "mamba": [],
108
+ "pip": []
109
+ }
110
+ }
111
+ `;
112
+ model = await contentsManager.save(model.path, {
113
+ ...model,
114
+ format: 'text',
115
+ size: undefined,
116
+ content: spkContent
117
+ });
118
+ }
119
+ });
120
+
121
+ commands.addCommand(dashCommandId, {
122
+ label: 'Dash App',
123
+ icon: dashIcon,
124
+ caption: 'Create a new Dash Application',
125
+ execute: async args => {
126
+ const cwd = args['cwd'] as string;
127
+ await generateAppFiles({
128
+ contentsManager: app.serviceManager.contents,
129
+ cwd,
130
+ framework: JupyterPackFramework.DASH
131
+ });
132
+ }
133
+ });
134
+
135
+ commands.addCommand(streamlitCommandId, {
136
+ label: 'Streamlit App',
137
+ icon: streamlitIcon,
138
+ caption: 'Create a new Streamlit Application',
139
+ execute: async args => {
140
+ const cwd = args['cwd'] as string;
141
+ await generateAppFiles({
142
+ contentsManager: app.serviceManager.contents,
143
+ cwd,
144
+ framework: JupyterPackFramework.STREAMLIT
145
+ });
146
+ }
147
+ });
148
+
149
+ commands.addCommand(shinyCommandId, {
150
+ label: 'Shiny App',
151
+ icon: shinyIcon,
152
+ caption: 'Create a new Shiny Application',
153
+ execute: async args => {
154
+ const cwd = args['cwd'] as string;
155
+ await generateAppFiles({
156
+ contentsManager: app.serviceManager.contents,
157
+ cwd,
158
+ framework: JupyterPackFramework.SHINY
159
+ });
160
+ }
161
+ });
162
+
163
+ launcher.add({
164
+ command: commandId,
165
+ category: 'JupyterPack',
166
+ rank: 1
167
+ });
168
+ launcher.add({
169
+ command: dashCommandId,
170
+ category: 'JupyterPack',
171
+ rank: 2
172
+ });
173
+ launcher.add({
174
+ command: streamlitCommandId,
175
+ category: 'JupyterPack',
176
+ rank: 3
177
+ });
178
+ launcher.add({
179
+ command: shinyCommandId,
180
+ category: 'JupyterPack',
181
+ rank: 4
182
+ });
183
+ }
184
+ };
@@ -0,0 +1,30 @@
1
+ export const DASH_APP = `
2
+ # This example is taken from the Dash official documentation (https://dash.plotly.com/layout)
3
+
4
+ import plotly.express as px
5
+ import pandas as pd
6
+ from dash import Dash, html, dcc
7
+ app = Dash()
8
+
9
+ # assume you have a "long-form" data frame
10
+ # see https://plotly.com/python/px-arguments/ for more options
11
+ df = pd.DataFrame({
12
+ "Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
13
+ "Amount": [4, 1, 2, 2, 4, 5],
14
+ "City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
15
+ })
16
+
17
+ fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
18
+
19
+ app.layout = html.Div(children=[
20
+ html.H1(children='Hello Dash'),
21
+
22
+ html.Div(children='''
23
+ Dash: A web application framework for your data.
24
+ '''),
25
+
26
+ dcc.Graph(
27
+ id='example-graph',
28
+ figure=fig
29
+ )
30
+ ])`;
@@ -0,0 +1,65 @@
1
+ import { Contents } from '@jupyterlab/services';
2
+ import { JupyterPackFramework } from '../../type';
3
+ import { DASH_APP } from './dash';
4
+ import { STREAMLIT_APP } from './streamlit';
5
+ import { SHINY_APP } from './shiny';
6
+
7
+ export async function generateAppFiles(options: {
8
+ contentsManager: Contents.IManager;
9
+ framework: JupyterPackFramework;
10
+ cwd: string;
11
+ }) {
12
+ const { contentsManager, framework, cwd } = options;
13
+
14
+ const pyModel = await contentsManager.newUntitled({
15
+ path: cwd,
16
+ type: 'file',
17
+ ext: '.py'
18
+ });
19
+ let appContent = '';
20
+ switch (framework) {
21
+ case JupyterPackFramework.DASH: {
22
+ appContent = DASH_APP;
23
+ break;
24
+ }
25
+ case JupyterPackFramework.STREAMLIT: {
26
+ appContent = STREAMLIT_APP;
27
+ break;
28
+ }
29
+ case JupyterPackFramework.SHINY: {
30
+ appContent = SHINY_APP;
31
+ break;
32
+ }
33
+ default:
34
+ break;
35
+ }
36
+ await contentsManager.save(pyModel.path, {
37
+ ...pyModel,
38
+ format: 'text',
39
+ size: undefined,
40
+ content: appContent
41
+ });
42
+
43
+ let model = await contentsManager.newUntitled({
44
+ path: cwd,
45
+ type: 'file',
46
+ ext: '.spk'
47
+ });
48
+
49
+ const spkContent = `
50
+ {
51
+ "name": "${model.name}",
52
+ "entry": "${pyModel.name}",
53
+ "framework": "${framework}",
54
+ "dependencies": {
55
+ "mamba": []
56
+ }
57
+ }
58
+ `;
59
+ model = await contentsManager.save(model.path, {
60
+ ...model,
61
+ format: 'text',
62
+ size: undefined,
63
+ content: spkContent
64
+ });
65
+ }
@@ -0,0 +1,49 @@
1
+ export const SHINY_APP = `
2
+ # This example is taken from the shinylive official playground (https://shinylive.io/py/examples/#modules)
3
+
4
+ from shiny import App, module, reactive, render, ui
5
+
6
+
7
+ # ============================================================
8
+ # Counter module
9
+ # ============================================================
10
+ @module.ui
11
+ def counter_ui(label: str = "Increment counter") -> ui.TagChild:
12
+ return ui.card(
13
+ ui.h2("This is " + label),
14
+ ui.input_action_button(id="button", label=label),
15
+ ui.output_code(id="out"),
16
+ )
17
+
18
+
19
+ @module.server
20
+ def counter_server(input, output, session, starting_value: int = 0):
21
+ count: reactive.value[int] = reactive.value(starting_value)
22
+
23
+ @reactive.effect
24
+ @reactive.event(input.button)
25
+ def _():
26
+ count.set(count() + 1)
27
+
28
+ @render.code
29
+ def out() -> str:
30
+ return f"Click count is {count()}"
31
+
32
+
33
+ # =============================================================================
34
+ # App that uses module
35
+ # =============================================================================
36
+ app_ui = ui.page_fluid(
37
+ counter_ui("counter1", "Counter 1"),
38
+ counter_ui("counter2", "Counter 2"),
39
+ )
40
+
41
+
42
+ def server(input, output, session):
43
+ counter_server("counter1")
44
+ counter_server("counter2")
45
+
46
+
47
+ app = App(app_ui, server)
48
+
49
+ `;
@@ -0,0 +1,26 @@
1
+ export const STREAMLIT_APP = `
2
+ # This example is taken from the streamlit official playground (https://streamlit.io/playground?example=charts)
3
+
4
+ import streamlit as st
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ st.write(
9
+ "Streamlit supports a wide range of data visualizations, including [Plotly, Altair, and Bokeh charts](https://docs.streamlit.io/develop/api-reference/charts). 📊 And with over 20 input widgets, you can easily make your data interactive!"
10
+ )
11
+
12
+ all_users = ["Alice", "Bob", "Charly"]
13
+ with st.container(border=True):
14
+ users = st.multiselect("Users", all_users, default=all_users)
15
+ rolling_average = st.toggle("Rolling average")
16
+
17
+ np.random.seed(42)
18
+ data = pd.DataFrame(np.random.randn(20, len(users)), columns=users)
19
+ if rolling_average:
20
+ data = data.rolling(7).mean().dropna()
21
+
22
+ tab1, tab2 = st.tabs(["Chart", "Dataframe"])
23
+ tab1.line_chart(data, height=250)
24
+ tab2.dataframe(data, height=250, width=True)
25
+
26
+ `;
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { spkPlugin } from './document/plugin';
1
+ import { launcherPlugin, spkPlugin } from './document/plugin';
2
2
  import { swPlugin } from './swConnection';
3
3
 
4
- export default [swPlugin, spkPlugin];
4
+ export default [swPlugin, spkPlugin, launcherPlugin];
package/src/tools.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import logoStr from '../style/icons/box.svg';
2
2
  import autoReloadStr from '../style/icons/autoreload.svg';
3
3
  import linkStr from '../style/icons/externallink.svg';
4
+ import dashStr from '../style/icons/Plotly-Logo-Black.svg';
5
+ import streamlitStr from '../style/icons/streamlit-logo-primary.svg';
6
+ import shinyStr from '../style/icons/shiny-for-python.svg';
4
7
  import { LabIcon } from '@jupyterlab/ui-components';
5
8
 
6
9
  export const IS_LITE = !!document.getElementById('jupyter-lite-main');
@@ -20,6 +23,20 @@ export const linkIcon = new LabIcon({
20
23
  svgstr: linkStr
21
24
  });
22
25
 
26
+ export const dashIcon = new LabIcon({
27
+ name: 'jupyterpack:dashBlack',
28
+ svgstr: dashStr
29
+ });
30
+
31
+ export const streamlitIcon = new LabIcon({
32
+ name: 'jupyterpack:streamlitBlack',
33
+ svgstr: streamlitStr
34
+ });
35
+
36
+ export const shinyIcon = new LabIcon({
37
+ name: 'jupyterpack:shinyLogo',
38
+ svgstr: shinyStr
39
+ });
23
40
  export function removePrefix(path: string, prefix: string): string {
24
41
  if (path.startsWith(prefix)) {
25
42
  return path.slice(prefix.length);
package/style/base.css CHANGED
@@ -56,3 +56,13 @@
56
56
  width: 0 !important;
57
57
  min-height: 0 !important;
58
58
  }
59
+
60
+ .jp-LauncherCard[data-category='JupyterPack'] {
61
+ .jp-LauncherCard-icon div {
62
+ width: 98% !important;
63
+ }
64
+
65
+ .jp-LauncherCard-icon svg {
66
+ width: 98% !important;
67
+ }
68
+ }
@@ -0,0 +1,60 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 300 100">
3
+ <defs>
4
+ <style>
5
+ .cls-1 {
6
+ fill: url(#linear-gradient);
7
+ }
8
+
9
+ .cls-1, .cls-2, .cls-3 {
10
+ stroke-width: 0px;
11
+ }
12
+
13
+ .cls-2 {
14
+ fill: #8c99cd;
15
+ }
16
+
17
+ .cls-3 {
18
+ fill: var(--jp-text-editor-icon-color, var(--jp-inverse-layout-color3));
19
+ }
20
+ </style>
21
+ <linearGradient id="linear-gradient" x1="44.87" y1="40.43" x2="102.87" y2="40.43" gradientTransform="translate(2 -3)" gradientUnits="userSpaceOnUse">
22
+ <stop offset="0" stop-color="#ff2c6d"/>
23
+ <stop offset="1" stop-color="#7fe4ff"/>
24
+ </linearGradient>
25
+ </defs>
26
+ <g>
27
+ <g>
28
+ <circle class="cls-2" cx="84.16" cy="45.71" r="4.14"/>
29
+ <circle class="cls-2" cx="100.73" cy="29.14" r="4.14"/>
30
+ <circle class="cls-2" cx="84.16" cy="29.14" r="4.14"/>
31
+ <circle class="cls-2" cx="67.59" cy="29.14" r="4.14"/>
32
+ <circle class="cls-2" cx="51.01" cy="29.14" r="4.14"/>
33
+ <circle class="cls-2" cx="51.01" cy="45.71" r="4.14"/>
34
+ </g>
35
+ <g>
36
+ <circle class="cls-1" cx="84.16" cy="45.71" r="4.14"/>
37
+ <circle class="cls-1" cx="100.73" cy="29.14" r="4.14"/>
38
+ <circle class="cls-1" cx="84.16" cy="29.14" r="4.14"/>
39
+ <circle class="cls-1" cx="67.59" cy="29.14" r="4.14"/>
40
+ <circle class="cls-1" cx="51.01" cy="29.14" r="4.14"/>
41
+ <circle class="cls-1" cx="51.01" cy="45.71" r="4.14"/>
42
+ </g>
43
+ </g>
44
+ <g>
45
+ <g>
46
+ <rect class="cls-3" x="157.34" y="32.94" width="5.78" height="34.68" rx="2.89" ry="2.89"/>
47
+ <rect class="cls-3" x="218.03" y="32.94" width="5.78" height="34.68" rx="2.89" ry="2.89"/>
48
+ <path class="cls-3" d="M139.99,38.72c-2.2,0-4.25.66-6,1.79-.43-1.05-1.47-1.79-2.67-1.79-1.6,0-2.89,1.29-2.89,2.89v28.9c0,1.6,1.29,2.89,2.89,2.89s2.89-1.29,2.89-2.89v-4.55c1.7,1.05,3.67,1.66,5.78,1.66,6.38,0,11.56-5.55,11.56-12.39v-4.13c0-6.84-5.18-12.39-11.56-12.39ZM145.78,55.54c0,3.48-2.59,6.31-5.78,6.31s-5.78-2.82-5.78-6.31v-4.73c0-3.48,2.59-6.31,5.78-6.31s5.78,2.82,5.78,6.31v4.73Z"/>
49
+ <path class="cls-3" d="M180.46,38.72h0c-6.38,0-11.56,5.55-11.56,12.39v4.13c0,6.84,5.18,12.39,11.56,12.39h0c6.38,0,11.56-5.55,11.56-12.39v-4.13c0-6.84-5.18-12.39-11.56-12.39ZM186.24,55.54c0,3.48-2.59,6.31-5.78,6.31s-5.78-2.82-5.78-6.31v-4.73c0-3.48,2.59-6.31,5.78-6.31s5.78,2.82,5.78,6.31v4.73Z"/>
50
+ <path class="cls-3" d="M209.36,61.84c-3.19,0-5.78-2.59-5.78-5.78v-11.56h5.78c1.6,0,2.89-1.29,2.89-2.89s-1.29-2.89-2.89-2.89h-5.78v-2.89c0-1.6-1.29-2.89-2.89-2.89s-2.89,1.29-2.89,2.89v20.23c0,6.37,5.19,11.56,11.56,11.56,1.6,0,2.89-1.29,2.89-2.89s-1.29-2.89-2.89-2.89Z"/>
51
+ <path class="cls-3" d="M249.95,38.96c-1.47-.63-3.17.05-3.8,1.52l-5.99,13.98-5.99-13.98c-.63-1.47-2.33-2.15-3.8-1.52-1.47.63-2.15,2.33-1.52,3.8l8.16,19.04-.97,2.26c-.88,2.11-2.92,3.51-5.21,3.56-1.6.04-2.86,1.36-2.82,2.96.04,1.57,1.32,2.82,2.89,2.82.02,0,.05,0,.07,0,4.57-.11,8.65-2.9,10.4-7.09l10.1-23.55c.63-1.47-.05-3.17-1.52-3.8Z"/>
52
+ </g>
53
+ <g>
54
+ <path class="cls-3" d="M51.01,58.14c-2.29,0-4.14,1.85-4.14,4.14v16.57c0,2.29,1.85,4.14,4.14,4.14s4.14-1.85,4.14-4.14v-16.57c0-2.29-1.85-4.14-4.14-4.14Z"/>
55
+ <path class="cls-3" d="M84.16,58.14c-2.29,0-4.14,1.85-4.14,4.14v16.57c0,2.29,1.85,4.14,4.14,4.14s4.14-1.85,4.14-4.14v-16.57c0-2.29-1.85-4.14-4.14-4.14Z"/>
56
+ <path class="cls-3" d="M67.59,41.57c-2.29,0-4.14,1.85-4.14,4.14v33.14c0,2.29,1.85,4.14,4.14,4.14s4.14-1.85,4.14-4.14v-33.14c0-2.29-1.85-4.14-4.14-4.14Z"/>
57
+ <path class="cls-3" d="M100.73,41.57c-2.29,0-4.14,1.85-4.14,4.14v33.14c0,2.29,1.85,4.14,4.14,4.14s4.14-1.85,4.14-4.14v-33.14c0-2.29-1.85-4.14-4.14-4.14Z"/>
58
+ </g>
59
+ </g>
60
+ </svg>
@@ -0,0 +1,82 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
4
+ y="0px" viewBox="0 0 154 30" style="enable-background:new 0 0 154 30;" xml:space="preserve" width="154" height="30">
5
+ <style type="text/css">
6
+ .st0 {
7
+ fill: var(--jp-text-editor-icon-color, var(--jp-inverse-layout-color3));
8
+ }
9
+ </style>
10
+ <g id="Layer_1_00000047768344360754847990000012721607800634040751_">
11
+ <g>
12
+ <path class="st0" d="M78,13.6h-1.7v6h-1v-6h-1.1v-0.5l1.1-0.4v-0.3c0-1.7,0.8-2.5,2.2-2.5c0.4,0,0.8,0.1,1.3,0.3l-0.3,0.8
13
+ c-0.4-0.1-0.8-0.3-1-0.3c-0.4,0-0.6,0.1-0.9,0.4s-0.3,0.6-0.3,1.3v0.4H78V13.6z" />
14
+ <path class="st0" d="M85,16.3c0,1.1-0.3,1.9-0.9,2.5c-0.5,0.6-1.3,0.9-2.3,0.9c-0.6,0-1.1-0.1-1.7-0.4c-0.5-0.3-0.9-0.6-1.1-1.3
15
+ c-0.3-0.5-0.4-1.1-0.4-1.9c0-1.1,0.3-1.9,0.8-2.5c0.5-0.6,1.3-0.9,2.3-0.9c0.9,0,1.7,0.3,2.3,0.9C84.7,14.2,85,15.1,85,16.3z
16
+ M79.7,16.3c0,0.9,0.1,1.5,0.5,1.9c0.4,0.5,0.9,0.6,1.5,0.6c0.6,0,1.1-0.3,1.5-0.6c0.4-0.4,0.5-1.1,0.5-1.9c0-0.9-0.1-1.5-0.5-1.9
17
+ c-0.4-0.4-0.9-0.6-1.5-0.6c-0.6,0-1.1,0.3-1.5,0.6C80,14.7,79.7,15.4,79.7,16.3z" />
18
+ <path class="st0" d="M89.8,12.7c0.3,0,0.5,0,0.8,0.1l-0.1,0.9c-0.3,0-0.5-0.1-0.8-0.1c-0.5,0-1,0.3-1.4,0.6s-0.6,1-0.6,1.7v3.6h-1
19
+ v-6.7h0.9l0.1,1.3l0,0c0.3-0.4,0.5-0.8,0.9-1C88.9,12.8,89.3,12.7,89.8,12.7z" />
20
+ <path class="st0" d="M104.8,10.7c0,1.3-0.4,2.3-1.3,2.9c-0.9,0.6-2.2,1-3.7,1h-1.5v5h-1.5V7h3.3C103.1,7,104.8,8.3,104.8,10.7z
21
+ M98.2,13.3h1.3c1.3,0,2.3-0.3,2.8-0.6c0.6-0.4,0.9-1.1,0.9-2c0-0.9-0.3-1.4-0.8-1.9c-0.5-0.4-1.4-0.6-2.5-0.6h-1.7
22
+ C98.2,8.1,98.2,13.3,98.2,13.3z" />
23
+ <path class="st0" d="M106.1,10.2h1.5l2,5.3c0.5,1.3,0.8,2.2,0.9,2.7h0.1c0.1-0.3,0.3-0.8,0.5-1.5c0.3-0.8,1-2.9,2.3-6.6h1.5
24
+ l-4.1,10.8c-0.4,1-0.9,1.8-1.4,2.3s-1.1,0.6-2,0.6c-0.4,0-0.9,0-1.3-0.1v-1.1c0.3,0.1,0.6,0.1,1,0.1c1,0,1.7-0.5,2.2-1.7l0.5-1.4
25
+ L106.1,10.2z" />
26
+ <path class="st0" d="M120.2,18.6c0.3,0,0.5,0,0.8,0s0.4-0.1,0.5-0.1v1.1c-0.1,0.1-0.4,0.1-0.6,0.1s-0.6,0.1-0.8,0.1
27
+ c-1.8,0-2.8-1-2.8-2.9v-5.7h-1.4v-0.6l1.4-0.6l0.6-2h0.9V10h2.8v1.1h-2.9v5.6c0,0.5,0.1,1,0.4,1.3
28
+ C119.3,18.4,119.6,18.6,120.2,18.6z" />
29
+ <path class="st0" d="M129.7,19.6v-6.1c0-0.8-0.1-1.4-0.5-1.8c-0.4-0.4-0.9-0.5-1.7-0.5c-1,0-1.8,0.3-2.2,0.8
30
+ c-0.5,0.5-0.6,1.4-0.6,2.7v5h-1.4V6.2h1.4v4.1c0,0.5,0,0.9-0.1,1.3h0.1c0.3-0.5,0.6-0.8,1.1-1s1.1-0.4,1.8-0.4
31
+ c1.1,0,2,0.3,2.5,0.8c0.6,0.5,0.9,1.4,0.9,2.7v6.1h-1.4V19.6z" />
32
+ <path class="st0" d="M141.9,14.9c0,1.5-0.4,2.8-1.1,3.6c-0.8,0.8-1.9,1.3-3.2,1.3c-0.9,0-1.7-0.3-2.3-0.6c-0.6-0.4-1.1-1-1.5-1.7
33
+ c-0.4-0.8-0.5-1.7-0.5-2.5c0-1.5,0.4-2.8,1.1-3.6c0.8-0.8,1.8-1.3,3.2-1.3c1.3,0,2.4,0.4,3.2,1.3
34
+ C141.5,12.2,141.9,13.3,141.9,14.9z M134.8,14.9c0,1.1,0.3,2.2,0.8,2.8c0.5,0.6,1.1,0.9,2.2,0.9c0.9,0,1.7-0.3,2.2-0.9
35
+ c0.5-0.6,0.8-1.5,0.8-2.8c0-1.1-0.3-2.2-0.8-2.8c-0.5-0.6-1.1-0.9-2.2-0.9c-0.9,0-1.7,0.3-2.2,0.9C135,12.7,134.8,13.6,134.8,14.9
36
+ z" />
37
+ <path class="st0" d="M150.7,19.6v-6.1c0-0.8-0.1-1.4-0.5-1.8c-0.4-0.4-0.9-0.5-1.7-0.5c-1,0-1.7,0.3-2.2,0.8
38
+ c-0.5,0.5-0.6,1.4-0.6,2.7v5h-1.4v-9.4h1.1l0.3,1.3h0.1c0.3-0.5,0.8-0.8,1.3-1.1c0.5-0.4,1.1-0.4,1.8-0.4c1.1,0,2,0.3,2.5,0.8
39
+ c0.6,0.5,0.9,1.4,0.9,2.7v6.1h-1.7V19.6z" />
40
+ </g>
41
+ </g>
42
+ <g id="Layer_2_00000147923674847605840940000016886061676261034887_">
43
+ <g id="Layer_1_00000115479941224969138130000004359990092642812304_">
44
+ <g>
45
+ <path class="st0" d="M67.4,15.3c-1.4,0.8-3.3,2.8-3.6,3.1c-1.1,1.1-2.3,2-3.5,2.7h-0.1c-0.1,0-0.1-0.1-0.1-0.2
46
+ c0.1-0.5,0.3-1.1,0.5-1.8c0.2-1,0.4-1.6,0.5-1.9s0.3-1,0.6-2.2s0.7-2.5,1.1-4c0.5-1.7,1.1-2.8,1.7-3.3l0,0c0.1,0,0.1-0.1,0.1-0.2
47
+ s-0.1-0.1-0.2-0.1l-1.3,0.1c-0.6,0-1.1,0.2-1.7,0.3C61.2,7.9,61,8,60.8,8.2c-0.2,0.2-0.3,0.3-0.4,0.4l0,0
48
+ c-0.1,0.2-0.3,0.4-0.3,0.6c-0.1,0.3-0.2,0.5-0.3,0.7c0,0.2-0.1,0.5-0.3,0.9c-0.1,0.4-0.2,0.8-0.3,1c-0.6,1.8-1.4,3.5-2.4,5.1
49
+ c-1,1.7-1.9,2.5-2.6,2.6l0,0c-0.3,0-0.4-0.3-0.5-1c-0.1-1.4,0.2-3.3,1-5.4c0.7-2.2,1.5-3.7,2.3-4.5c0.1,0.1,0.2-0.1,0.2-0.2l0,0
50
+ c0-0.1-0.1-0.2-0.3-0.2c-1.1,0.1-2,0.3-2.6,0.6c-0.6,0.4-1.2,1.1-1.8,2.2c-0.5,0.9-0.9,2.1-1.2,3.6l0,0c-0.1,0.1-0.1,0.3-0.2,0.4
51
+ c-0.3,0.5-0.5,1.1-0.9,1.8c-0.3,0.6-0.6,1.2-0.9,1.7c-0.3,0.5-0.7,0.9-1.1,1.3c-0.5,0.5-0.8,0.6-1,0.7l0,0
52
+ c-0.2,0-0.4-0.1-0.4-0.7c-0.1-0.8,0.3-2.6,1-5.2c0.4-1.2,0.6-2,0.6-2.4c0.1-0.4,0.1-0.8,0.1-1.2c-0.1-1.3-0.8-1.9-2-1.8
53
+ c-1.6,0.1-3.1,0.9-4.6,2.5h-0.1c-0.1,0-0.1-0.1-0.1-0.1c0.1-0.3,0.1-0.6,0.1-0.9c0-0.3-0.2-0.6-0.4-0.7c-0.2-0.2-0.7-0.2-1.5-0.2
54
+ l-1.3,0.1c0,0,0,0-0.1,0s-0.1,0.1,0,0.2c0.1,0.1,0.1,0.3,0.1,0.4c0.1,0.9-0.2,2.6-0.8,4.9l-0.2,0.3c-0.1,0.1-0.2,0.3-0.4,0.8
55
+ c-0.3,0.5-0.5,1.1-0.9,1.8c-0.3,0.7-0.7,1.3-0.9,1.8c-0.3,0.5-0.7,0.9-1.1,1.3c-0.6,0.5-0.9,0.6-1,0.6l0,0c-0.1,0-0.4,0-0.5-0.8
56
+ c0-0.6,0.1-1.5,0.3-2.6c0.3-1.1,0.5-1.9,0.8-2.5c0.2-0.6,0.6-1.5,1.1-2.8c0.5-1.2,0.9-2,1.2-2.3v-0.1c0-0.1-0.1-0.1-0.1-0.1
57
+ L33.9,11c-0.9,0.1-1.8,0.8-2.4,2.3c-0.6,1.3-1,2.6-1.4,3.9c0,0,0,0,0,0.1c-1.6,4-3,6.1-4.2,6.2l0,0c-0.1,0-0.4,0-0.4-0.7
58
+ c0-0.6,0-1.3,0.3-2.3c0.1-0.4,0.2-0.9,0.3-1.4c0.2-0.6,0.3-1,0.4-1.3s0.2-0.7,0.4-1.3c0.2-0.5,0.3-1,0.4-1.4s0.2-0.9,0.1-1.3
59
+ c-0.1-1.2-0.8-1.8-2-1.7c-0.7,0.1-1.3,0.2-1.9,0.5c-0.4,0.2-0.8,0.4-1.1,0.7c0,0-0.1,0-0.2,0c0,0,0-0.1,0-0.2
60
+ c2-3,3.5-5.6,4.4-7.9c0.5-1.3,0.7-2.3,0.7-3.1c-0.1-1.1-0.7-1.7-1.8-1.6c-1.1,0.1-2,0.6-2.9,1.6c-0.9,1.1-1.7,2.3-2.3,3.6
61
+ c-0.7,1.7-1.4,3.4-1.9,5.1c-0.6,2-1.4,4.8-2.2,8.3c-0.8,3.5-1.5,6.1-1.9,7.7l0,0c0,0.1,0.1,0.1,0.1,0.1l3.1-0.2v-0.1
62
+ c0.1-0.7,0.3-1.3,0.4-1.9c0.1-0.5,0.3-1.3,0.5-2.1s0.4-1.6,0.5-2.2s0.4-1.3,0.7-2.2c0.3-0.8,0.7-1.5,1.2-2.2
63
+ c0.4-0.6,1-1.1,1.6-1.5c0.7-0.4,1-0.5,1.2-0.5l0,0c0.2,0,0.4,0.1,0.4,0.5c0,0.3-0.1,0.9-0.3,1.7c-1.1,3.4-1.6,6.1-1.5,7.9
64
+ c0.1,1.6,0.8,2.4,2.1,2.3c1.5-0.1,3.1-1.8,4.9-5c0-0.1,0.1-0.1,0.2-0.1s0.1,0.1,0.1,0.2c0,0.4,0,0.7,0,1.1
65
+ c0.1,1.8,0.8,2.7,1.9,2.7h0.1c0.7,0,1.5-0.5,2.2-1.2c0.3-0.3,0.7-0.7,1-1.1c0.5-0.6,0.7-1,1.3-2c-0.1,0.3-0.7,2.5-0.6,3.1
66
+ l3.3-0.2v-0.1c0-0.1,0.1-0.7,0.2-1.2c0.1-0.7,0.3-1.4,0.4-2c0.1-0.6,0.3-1.4,0.6-2.4c0.2-0.9,0.5-1.7,0.8-2.5
67
+ c0.3-0.7,0.7-1.5,1.1-2.2c0.3-0.6,0.8-1.2,1.3-1.7c0.4-0.4,1-0.6,1.5-0.6s0.7,0.1,0.7,0.5c0,0.2,0,0.4-0.1,0.6
68
+ c-1.3,4.6-1.9,7.6-1.8,9.1c0.1,1.6,0.8,2.3,2.1,2.2c0.7,0,1.5-0.5,2.2-1.2c0.8-0.8,1.5-1.7,2-2.6c0.2-0.3,0.4-0.7,0.6-1
69
+ c0-0.1,0.1-0.1,0.2-0.1c0.1,0,0.1,0.1,0.1,0.2c0,0.2,0,0.4,0,0.5c0,1,0.3,1.7,0.7,2.2s0.9,0.7,1.5,0.7c0.5,0,1-0.1,1.5-0.3
70
+ c0.4-0.2,1-0.6,1.7-1.3c0.5-0.6,1-1.2,1.5-1.9c0,0,0.1-0.1,0.1,0c0.1,0,0.1,0.1,0.1,0.2l-1.1,3.6c0,0,0,0.1-0.1,0.1
71
+ c-19.6,2.9-2.9,14.1,3.1,0.4c0,0,0-0.1,0.1-0.1s0.2-0.1,0.3-0.1c1.6-0.8,2.5-1.4,4.5-3.3C66.5,17.7,66.6,16.8,67.4,15.3z
72
+ M20.7,12.9c0,0-0.1,0.1-0.1,0c-0.1,0-0.1-0.1-0.1-0.2c0.8-2.7,1.6-5.2,2.5-7.4c1.2-3.2,2-3.9,2.5-3.9l0,0c0.1,0,0.3,0,0.3,0.5
73
+ c0,0.4-0.2,1.4-0.7,2.8C24.3,7,22.8,9.7,20.7,12.9z M50,27.2c-0.6-1.3,5.4-2.9,5.9-3C54.8,26.7,50.7,28.7,50,27.2z" />
74
+ <path class="st0" d="M35,8.6c0.8,0,1.7-0.3,1.9-1.4c0.1-0.5-0.2-1.1-0.4-1.3s-0.4-0.2-0.8-0.2c-0.3,0-0.8,0.2-1.1,0.6
75
+ c-0.5,0.5-0.5,1.2-0.3,1.6C34.4,8.3,34.7,8.6,35,8.6z" />
76
+ <path class="st0" d="M3.7,27.5c0.5,0,8.1,1.7,10.2-5.8c0.8-3.2-3-7.3-3.3-7.8l0,0C6,9.2,8.4,4.5,11.6,5c2.3,0.4,1.1,3.8,2.4,4.1
77
+ l0,0c0.7,0.1,2.2-1.7,2.4-2.3c0.8-2.3-4-3.8-5.4-3.7C8,3.4,6.1,5,5,7.6c-2.2,4.8,1.9,8,4.3,11.3c1.8,2.5,1.8,4.7-0.5,6.9
78
+ c-2.5,2.4-5-1-5.3-3.3c-0.3-2.1-0.6-3.5-2.4-0.8C-0.6,24.2,1.7,27.2,3.7,27.5z" />
79
+ </g>
80
+ </g>
81
+ </g>
82
+ </svg>
@@ -0,0 +1,14 @@
1
+ <svg width="1461" height="406" viewBox="0 0 1461 406" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ d="M258.73 221.683L206.138 193.883L114.846 145.633C114.763 145.549 114.596 145.549 114.513 145.549C111.18 143.966 107.763 147.299 109.013 150.633L155.53 269.275L155.538 269.3C155.588 269.416 155.63 269.533 155.68 269.65C157.588 274.075 161.755 276.808 166.288 277.883C166.672 277.966 166.946 278.042 167.406 278.134C167.864 278.236 168.505 278.375 169.055 278.416C169.147 278.425 169.23 278.425 169.322 278.433H169.388C169.455 278.441 169.522 278.441 169.588 278.45H169.68C169.738 278.458 169.805 278.458 169.863 278.458H169.972C170.038 278.466 170.105 278.466 170.172 278.466V278.466C229.083 284.889 288.519 284.889 347.43 278.466V278.466C348.139 278.466 348.83 278.433 349.497 278.366C349.714 278.341 349.922 278.316 350.13 278.291C350.155 278.283 350.189 278.283 350.214 278.275C350.355 278.258 350.497 278.233 350.639 278.208C350.847 278.183 351.055 278.141 351.264 278.1C351.68 278.008 351.87 277.941 352.435 277.747C353.001 277.553 353.939 277.213 354.527 276.93C355.114 276.646 355.522 276.375 356.014 276.066C356.622 275.683 357.201 275.291 357.787 274.851C358.04 274.657 358.214 274.533 358.397 274.358L358.297 274.3L258.73 221.683Z"
4
+ fill="#FF4B4B" />
5
+ <path
6
+ d="M402.766 145.634H402.683L311.357 193.884L362.124 269.493L408.524 150.634V150.467C409.691 146.967 406.108 143.801 402.766 145.634"
7
+ fill="#7D353B" />
8
+ <path
9
+ d="M263.597 122.691C261.264 119.283 256.18 119.283 253.93 122.691L206.139 193.883L258.73 221.684L358.398 274.358C359.023 273.745 359.525 273.148 360.056 272.517C360.806 271.592 361.506 270.6 362.123 269.492L311.356 193.883L263.597 122.691Z"
10
+ fill="#BD4043" />
11
+ <path
12
+ d="M583.705 163.429H602.096C601.253 139.75 579.413 122.048 549.067 122.048C519.027 122.048 495.425 139.52 495.425 165.881C495.425 187.032 510.751 199.599 535.273 206.649L554.585 212.167C571.137 216.765 585.85 222.589 585.85 238.222C585.85 255.387 569.298 266.729 547.534 266.729C528.836 266.729 512.284 258.453 510.751 240.674H491.133C492.972 266.422 513.816 283.894 547.534 283.894C583.705 283.894 604.242 263.97 604.242 238.528C604.242 209.102 576.348 199.599 560.102 195.308L544.163 191.016C532.514 187.951 513.816 181.821 513.816 164.962C513.816 149.942 527.61 138.907 548.454 138.907C567.459 138.907 581.866 147.949 583.705 163.429ZM679.911 163.429H654.776V135.228H636.69V163.429H618.912V178.755H636.69V252.322C636.69 272.859 653.243 282.668 668.569 282.668C675.313 282.668 679.604 281.442 682.057 280.523L678.378 264.277C676.846 264.583 674.393 265.196 670.408 265.196C662.439 265.196 654.776 262.744 654.776 247.418V178.755H679.911V163.429ZM702.799 281.136H720.884V206.649C720.884 190.71 733.452 179.062 750.618 179.062C755.446 179.062 760.427 179.981 761.653 180.288V161.896C759.584 161.743 754.832 161.59 752.15 161.59C738.05 161.59 725.789 169.559 721.498 181.208H720.271V163.429H702.799V281.136ZM824.543 283.588C848.452 283.588 865.924 271.633 871.442 253.855L853.97 248.95C849.372 261.211 838.72 267.342 824.543 267.342C803.316 267.342 788.679 253.625 787.836 228.413H873.281V220.75C873.281 176.916 847.226 161.896 822.704 161.896C790.825 161.896 769.675 187.032 769.675 223.202C769.675 259.372 790.518 283.588 824.543 283.588ZM787.836 212.78C789.062 194.465 802.013 178.142 822.704 178.142C842.322 178.142 854.889 192.856 854.889 212.78H787.836ZM931.114 283.894C951.651 283.894 962.38 272.859 966.058 265.196H966.978V281.136H985.063V203.584C985.063 166.188 956.556 161.896 941.536 161.896C923.757 161.896 903.526 168.027 894.33 189.484L911.496 195.614C915.481 187.032 924.907 177.836 942.149 177.836C958.778 177.836 966.978 186.648 966.978 201.745V202.358C966.978 211.094 958.088 210.328 936.631 213.086C914.791 215.922 890.959 220.75 890.959 247.724C890.959 270.714 908.737 283.894 931.114 283.894ZM933.873 267.649C919.466 267.649 909.044 261.211 909.044 248.644C909.044 234.85 921.611 230.559 935.712 228.719C943.375 227.8 963.912 225.654 966.978 221.976V238.528C966.978 253.242 955.329 267.649 933.873 267.649ZM1013.76 281.136H1031.84V207.569C1031.84 190.327 1044.41 178.142 1058.51 178.142C1072.23 178.142 1081.81 187.108 1081.81 200.519V281.136H1100.2V204.504C1100.2 189.331 1109.7 178.142 1126.25 178.142C1139.13 178.142 1150.16 184.963 1150.16 202.358V281.136H1168.25V202.358C1168.25 174.694 1153.38 161.896 1132.38 161.896C1115.52 161.896 1103.19 169.636 1097.13 181.821H1095.91C1090.08 169.253 1079.81 161.896 1064.33 161.896C1049.01 161.896 1037.67 169.253 1032.76 181.821H1031.23V163.429H1013.76V281.136ZM1215.08 124.193H1197V281.136H1215.08V124.193ZM1243.89 281.136H1261.98V163.429H1243.89V281.136ZM1253.09 143.811C1260.14 143.811 1265.96 138.294 1265.96 131.55C1265.96 124.806 1260.14 119.289 1253.09 119.289C1246.04 119.289 1240.21 124.806 1240.21 131.55C1240.21 138.294 1246.04 143.811 1253.09 143.811ZM1341.97 163.429H1316.84V135.228H1298.75V163.429H1280.98V178.755H1298.75V252.322C1298.75 272.859 1315.31 282.668 1330.63 282.668C1337.38 282.668 1341.67 281.442 1344.12 280.523L1340.44 264.277C1338.91 264.583 1336.46 265.196 1332.47 265.196C1324.5 265.196 1316.84 262.744 1316.84 247.418V178.755H1341.97V163.429Z"
13
+ fill="var(--jp-text-editor-icon-color, var(--jp-inverse-layout-color3))" />
14
+ </svg>