├── .gitignore
├── LICENCE.txt
├── MANIFEST.in
├── README.md
├── docs
└── presentation
│ ├── An Elm kernel for Jupyter.ipynb
│ ├── README.md
│ ├── images
│ ├── cooper-thumbsup.jpeg
│ ├── elm-logo.png
│ └── squarelogo-greytext-orangebody-greymoons.png
│ ├── requirements.txt
│ └── theme
│ ├── sixty-north-device.png
│ ├── sixty-north-logo.png
│ ├── sixty-north-logo.small.png
│ └── sixty_north.css
├── elm_kernel
├── __init__.py
├── __main__.py
├── install.py
└── kernel.py
├── examples
├── hello-world.ipynb
├── images
│ └── tea.png
├── svg
│ ├── SVG example.ipynb
│ └── elm.json
└── the-elm-architecture.ipynb
├── kernel.json
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | MANIFEST
3 | .ipynb_checkpoints
4 | dist
5 | build
6 | .ropeproject
7 | *.egg-info
8 | .vscode
9 |
--------------------------------------------------------------------------------
/LICENCE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 Austin Bingham
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This kernel adds support for Elm to [Jupyter](http://jupyter.org/) notebooks.
2 |
3 | While basic functionality is in place, this is still very much a work in
4 | progress. I'm still figuring it all out. Any help, ideas, etc. would be great.
5 |
6 | # Requirements
7 | - Python 3.6+
8 |
9 | # Installation
10 |
11 | Either install from a repository using `pip`:
12 |
13 | ```
14 | pip install elm_kernel
15 | ```
16 |
17 | or install the package from source:
18 |
19 | ```
20 | pip install -e .
21 | ```
22 |
23 | Then install the kernel spec:
24 | ```
25 | python -m elm_kernel.install
26 | ```
27 |
28 | # Usage
29 |
30 | Run `jupyter notebook` and select the Elm kernel for a new notebook.
31 |
32 | ## Multi-cell code examples
33 |
34 | By default, when you execute a code cell with the Elm kernel the code will *not*
35 | be compiled. Instead, the kernel simply queues up code cells. This way you can
36 | break longer examples over multiple cells, interleaving the code cells with
37 | supporting Markdown cells.
38 |
39 | In order to ask the kernel to actually compile your code, you need to terminate
40 | a code cell with the line:
41 |
42 | ```
43 | -- compile-code
44 | ```
45 |
46 | When the kernel sees a cell like this it contatenates, in cell-execution order,
47 | all of the executed but uncompiled code cells (i.e. everything since the start
48 | of the kernel or the last `-- compile-code` cell). It then compiles the
49 | concatenated code, returning the result to the notebook.
50 |
51 | For a concrete example of this, see
52 | [`examples/the-elm-architecture.ipynb`](https://github.com/abingham/jupyter-elm-kernel/blob/master/examples/the-elm-architecture.ipynb).
53 |
54 | This is a bit hacky, and we're actively searching for a better alternative.
55 | Ideas are welcome!
56 |
57 | # Examples
58 |
59 | The `examples` directory contains a few examples of how to use this kernel. Just
60 | go to that directory and run `jupyter notebook` to see them.
61 |
--------------------------------------------------------------------------------
/docs/presentation/An Elm kernel for Jupyter.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 10,
6 | "metadata": {
7 | "collapsed": false,
8 | "deletable": true,
9 | "editable": true,
10 | "slideshow": {
11 | "slide_type": "skip"
12 | }
13 | },
14 | "outputs": [
15 | {
16 | "data": {
17 | "text/html": [
18 | " "
19 | ],
20 | "text/plain": [
21 | ""
22 | ]
23 | },
24 | "metadata": {},
25 | "output_type": "display_data"
26 | }
27 | ],
28 | "source": [
29 | "%%html\n",
30 | " "
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {
36 | "deletable": true,
37 | "editable": true,
38 | "slideshow": {
39 | "slide_type": "slide"
40 | }
41 | },
42 | "source": [
43 | "# The Jupyter Elm Kernel\n",
44 | "## Interactive notebooks for Elm\n",
45 | "\n",
46 | "\n",
47 | "\n",
48 | " \n",
49 | " \n",
50 | "\n",
51 | " \n",
52 | " \n",
53 | "
\n",
54 | "\n",
55 | "----\n",
56 | "\n",
57 | "**Austin Bingham** \n",
58 | "*twitter:* @austin_bingham \n",
59 | "*email:* austin@sixty-north.com\n",
60 | "\n",
61 | "----\n",
62 | "\n",
63 | ""
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {
69 | "deletable": true,
70 | "editable": true,
71 | "slideshow": {
72 | "slide_type": "slide"
73 | }
74 | },
75 | "source": [
76 | "# What is a Jupyter notebook?\n",
77 | "\n",
78 | "> The Jupyter Notebook is an open-source web application that allows you to create and share documents that contain live code, equations, visualizations and explanatory text.\n",
79 | ">\n",
80 | "> -- jupyter.org "
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "metadata": {
86 | "deletable": true,
87 | "editable": true,
88 | "slideshow": {
89 | "slide_type": "fragment"
90 | }
91 | },
92 | "source": [
93 | "Let's look at [an example showing the Elm architecture](../../examples/the-elm-architecture.ipynb)."
94 | ]
95 | },
96 | {
97 | "cell_type": "markdown",
98 | "metadata": {
99 | "deletable": true,
100 | "editable": true,
101 | "slideshow": {
102 | "slide_type": "slide"
103 | }
104 | },
105 | "source": [
106 | "# What is a Jupyter kernel?\n",
107 | "> A ‘kernel’ is a program that runs and introspects the user’s code.\n",
108 | ">\n",
109 | "> -- ipython.org \n",
110 | "\n",
111 | "- Receives code from the client when a cell is executed\n",
112 | "- Runs the code (for some definition of \"run\")\n",
113 | "- Returns output and status of execution"
114 | ]
115 | },
116 | {
117 | "cell_type": "markdown",
118 | "metadata": {
119 | "deletable": true,
120 | "editable": true,
121 | "slideshow": {
122 | "slide_type": "slide"
123 | }
124 | },
125 | "source": [
126 | "# The Elm kernel\n",
127 | "## Support for Elm code cells in Jupyter notebooks\n",
128 | "----\n",
129 | "- Accumulates code cells\n",
130 | "- Compiles accumulated code on \"`-- compile-code`\"\n",
131 | "- Compilation goes into a temporary file\n",
132 | "- Compilaton results are shipped back to the web client\n",
133 | "- `elm-stuff` if kept between compilations"
134 | ]
135 | },
136 | {
137 | "cell_type": "markdown",
138 | "metadata": {
139 | "deletable": true,
140 | "editable": true,
141 | "slideshow": {
142 | "slide_type": "slide"
143 | }
144 | },
145 | "source": [
146 | "# Kernel initialization\n",
147 | "----\n",
148 | "```python\n",
149 | "class ElmKernel(Kernel):\n",
150 | " def __init__(self, *args, **kwargs):\n",
151 | " super().__init__(*args, **kwargs)\n",
152 | " self._code = []\n",
153 | " self._tempdir = TemporaryDirectory()\n",
154 | "```"
155 | ]
156 | },
157 | {
158 | "cell_type": "markdown",
159 | "metadata": {
160 | "deletable": true,
161 | "editable": true,
162 | "slideshow": {
163 | "slide_type": "slide"
164 | }
165 | },
166 | "source": [
167 | "# Compilation and execution\n",
168 | "## Receive code from the web client\n",
169 | "----\n",
170 | "```python\n",
171 | "def do_execute(self, code, . . .):\n",
172 | " self._code.append(code)\n",
173 | " if self._should_compile:\n",
174 | " try:\n",
175 | " code = \"\\n\".join(self._code)\n",
176 | " self._code = []\n",
177 | " self._compile(code)\n",
178 | " except Exception as exc:\n",
179 | " self._send_error_result(str(exc))\n",
180 | " return {'status': 'error' . . . }\n",
181 | " return {'status': 'ok' . . . }\n",
182 | "```"
183 | ]
184 | },
185 | {
186 | "cell_type": "markdown",
187 | "metadata": {
188 | "deletable": true,
189 | "editable": true,
190 | "slideshow": {
191 | "slide_type": "subslide"
192 | }
193 | },
194 | "source": [
195 | "# Compilation and execution\n",
196 | "## Write code to file and compile it, reporting any failures\n",
197 | "----\n",
198 | "```python\n",
199 | "def _compile(self, code):\n",
200 | " with self._tempfile('input.elm') as infile,\\\n",
201 | " self._tempfile('index.js') as outfile:\n",
202 | " with open(infile, mode='wt') as f:\n",
203 | " f.write(code)\n",
204 | " try:\n",
205 | " # compile in a subprocess (next slide)\n",
206 | " except subprocess.CalledProcessError as err:\n",
207 | " self._send_error_result(err.stdout)\n",
208 | " except Exception as err:\n",
209 | " self._send_error_result(repr(err))\n",
210 | " raise\n",
211 | "```"
212 | ]
213 | },
214 | {
215 | "cell_type": "markdown",
216 | "metadata": {
217 | "deletable": true,
218 | "editable": true,
219 | "slideshow": {
220 | "slide_type": "subslide"
221 | }
222 | },
223 | "source": [
224 | "# Compilation and execution\n",
225 | "## Run compiler, read output, and report success\n",
226 | "----\n",
227 | "```python\n",
228 | "subprocess.run(\n",
229 | " ['elm-make', infile, '--yes', '--output={}'.format(outfile)],\n",
230 | " cwd=self._tempdir.name,\n",
231 | " check=True,\n",
232 | " stdout=subprocess.PIPE,\n",
233 | " stderr=subprocess.STDOUT,\n",
234 | " encoding=sys.getdefaultencoding())\n",
235 | "\n",
236 | "with open(outfile, mode='rt') as f:\n",
237 | " javascript = f.read()\n",
238 | "\n",
239 | "self._send_success_result(javascript)\n",
240 | "```"
241 | ]
242 | },
243 | {
244 | "cell_type": "markdown",
245 | "metadata": {
246 | "deletable": true,
247 | "editable": true,
248 | "slideshow": {
249 | "slide_type": "slide"
250 | }
251 | },
252 | "source": [
253 | "# Reporting success\n",
254 | "## Producing JavaScript embedding code\n",
255 | "----\n",
256 | "```python\n",
257 | "module_name = \"Main\"\n",
258 | "div_id = 'elm-div-' + str(self.execution_count)\n",
259 | "template = \"\"\"\n",
260 | " var defineElm = function(cb) {{\n",
261 | " if (this.Elm) {{\n",
262 | " this.oldElm = this.Elm;\n",
263 | " }}\n",
264 | " var define = null;\n",
265 | " {js}\n",
266 | " cb();\n",
267 | " }};\n",
268 | " var obj = new Object();\n",
269 | " defineElm.bind(obj)(function(){{\n",
270 | " var mountNode = document.getElementById('{div_id}');\n",
271 | " obj.Elm. {module_name}.embed(mountNode);\n",
272 | " }});\"\"\"\n",
273 | "```"
274 | ]
275 | },
276 | {
277 | "cell_type": "markdown",
278 | "metadata": {
279 | "deletable": true,
280 | "editable": true,
281 | "slideshow": {
282 | "slide_type": "subslide"
283 | }
284 | },
285 | "source": [
286 | "# Reporting success\n",
287 | "## Injecting HTML into the client\n",
288 | "----\n",
289 | "```python\n",
290 | "self.send_response(\n",
291 | " self.iopub_socket,\n",
292 | " 'display_data',\n",
293 | " {\n",
294 | " 'metadata': {},\n",
295 | " 'data': {\n",
296 | " 'text/html': '
'\n",
297 | " }\n",
298 | " }\n",
299 | ")\n",
300 | "```"
301 | ]
302 | },
303 | {
304 | "cell_type": "markdown",
305 | "metadata": {
306 | "deletable": true,
307 | "editable": true,
308 | "slideshow": {
309 | "slide_type": "subslide"
310 | }
311 | },
312 | "source": [
313 | "# Reporting success\n",
314 | "## Sending JavaScript to client\n",
315 | "----\n",
316 | "```python\n",
317 | "javascript = template.format(\n",
318 | " js=javascript,\n",
319 | " module_name=module_name,\n",
320 | " div_id=div_id)\n",
321 | "\n",
322 | "self.send_response(\n",
323 | " self.iopub_socket,\n",
324 | " 'display_data',\n",
325 | " {\n",
326 | " 'metadata': {},\n",
327 | " 'data': {\n",
328 | " 'application/javascript': javascript\n",
329 | " }\n",
330 | " })\n",
331 | "```"
332 | ]
333 | },
334 | {
335 | "cell_type": "markdown",
336 | "metadata": {
337 | "deletable": true,
338 | "editable": true,
339 | "slideshow": {
340 | "slide_type": "slide"
341 | }
342 | },
343 | "source": [
344 | "# Good news! It seems to work!\n",
345 | "\n",
346 | ""
347 | ]
348 | },
349 | {
350 | "cell_type": "markdown",
351 | "metadata": {
352 | "collapsed": true,
353 | "deletable": true,
354 | "editable": true,
355 | "slideshow": {
356 | "slide_type": "slide"
357 | }
358 | },
359 | "source": [
360 | "# Room for improvement\n"
361 | ]
362 | },
363 | {
364 | "cell_type": "markdown",
365 | "metadata": {
366 | "deletable": true,
367 | "editable": true,
368 | "slideshow": {
369 | "slide_type": "fragment"
370 | }
371 | },
372 | "source": [
373 | "- Improved way of signalling for compilation"
374 | ]
375 | },
376 | {
377 | "cell_type": "markdown",
378 | "metadata": {
379 | "slideshow": {
380 | "slide_type": "fragment"
381 | }
382 | },
383 | "source": [
384 | "- Some way to re-use code cells"
385 | ]
386 | },
387 | {
388 | "cell_type": "markdown",
389 | "metadata": {
390 | "deletable": true,
391 | "editable": true,
392 | "slideshow": {
393 | "slide_type": "fragment"
394 | }
395 | },
396 | "source": [
397 | "- Better support for `elm-package.json`"
398 | ]
399 | },
400 | {
401 | "cell_type": "markdown",
402 | "metadata": {
403 | "deletable": true,
404 | "editable": true,
405 | "slideshow": {
406 | "slide_type": "fragment"
407 | }
408 | },
409 | "source": [
410 | "- Automated tests / travis-ci "
411 | ]
412 | },
413 | {
414 | "cell_type": "markdown",
415 | "metadata": {
416 | "deletable": true,
417 | "editable": true,
418 | "slideshow": {
419 | "slide_type": "fragment"
420 | }
421 | },
422 | "source": [
423 | "- An \"elm-repl\" experience"
424 | ]
425 | },
426 | {
427 | "cell_type": "markdown",
428 | "metadata": {
429 | "deletable": true,
430 | "editable": true,
431 | "slideshow": {
432 | "slide_type": "slide"
433 | }
434 | },
435 | "source": [
436 | "# Get involved\n",
437 | "\n",
438 | "You can find everything at the github project page: https://github.com/abingham/jupyter-elm-kernel"
439 | ]
440 | },
441 | {
442 | "cell_type": "markdown",
443 | "metadata": {
444 | "deletable": true,
445 | "editable": true,
446 | "slideshow": {
447 | "slide_type": "fragment"
448 | }
449 | },
450 | "source": [
451 | "You can help with:\n",
452 | "\n",
453 | "- Feedback about actual use"
454 | ]
455 | },
456 | {
457 | "cell_type": "markdown",
458 | "metadata": {
459 | "deletable": true,
460 | "editable": true,
461 | "slideshow": {
462 | "slide_type": "fragment"
463 | }
464 | },
465 | "source": [
466 | "- Feature ideas and bug reports"
467 | ]
468 | },
469 | {
470 | "cell_type": "markdown",
471 | "metadata": {
472 | "deletable": true,
473 | "editable": true,
474 | "slideshow": {
475 | "slide_type": "fragment"
476 | }
477 | },
478 | "source": [
479 | "- Pull requests"
480 | ]
481 | },
482 | {
483 | "cell_type": "markdown",
484 | "metadata": {
485 | "deletable": true,
486 | "editable": true,
487 | "slideshow": {
488 | "slide_type": "slide"
489 | }
490 | },
491 | "source": [
492 | "# Thanks!\n",
493 | "\n",
494 | "----\n",
495 | "\n",
496 | " - Presentation: [github.com/abingham/jupyter-elm-kernel](https://github.com/abingham/jupyter-elm-kernel)\n",
497 | " - The Python Apprentice: [leanpub.com/python-apprentice/c/oslo-elm-day-2017](https://leanpub.com/python-apprentice/c/oslo-elm-day-2017)\n",
498 | " - Training and consulting: [sixty-north.com](http://sixty-north.com/)\n",
499 | " \n",
500 | " ----\n",
501 | " \n",
502 | ""
503 | ]
504 | }
505 | ],
506 | "metadata": {
507 | "celltoolbar": "Slideshow",
508 | "kernelspec": {
509 | "display_name": "Python 3",
510 | "language": "python",
511 | "name": "python3"
512 | },
513 | "language_info": {
514 | "codemirror_mode": {
515 | "name": "ipython",
516 | "version": 3
517 | },
518 | "file_extension": ".py",
519 | "mimetype": "text/x-python",
520 | "name": "python",
521 | "nbconvert_exporter": "python",
522 | "pygments_lexer": "ipython3",
523 | "version": "3.6.0"
524 | }
525 | },
526 | "nbformat": 4,
527 | "nbformat_minor": 2
528 | }
529 |
--------------------------------------------------------------------------------
/docs/presentation/README.md:
--------------------------------------------------------------------------------
1 | To run this presentation, do this (probably in a virtualenv):
2 |
3 | ```
4 | pip install -r requirements.txt
5 | jupyter nbextension install rise --py --sys-prefix
6 | jupyter nbextension enable rise --py --sys-prefix
7 | jupyter notebook
8 | ```
9 |
--------------------------------------------------------------------------------
/docs/presentation/images/cooper-thumbsup.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/images/cooper-thumbsup.jpeg
--------------------------------------------------------------------------------
/docs/presentation/images/elm-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/images/elm-logo.png
--------------------------------------------------------------------------------
/docs/presentation/images/squarelogo-greytext-orangebody-greymoons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/images/squarelogo-greytext-orangebody-greymoons.png
--------------------------------------------------------------------------------
/docs/presentation/requirements.txt:
--------------------------------------------------------------------------------
1 | jupyter
2 | rise
3 |
--------------------------------------------------------------------------------
/docs/presentation/theme/sixty-north-device.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/theme/sixty-north-device.png
--------------------------------------------------------------------------------
/docs/presentation/theme/sixty-north-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/theme/sixty-north-logo.png
--------------------------------------------------------------------------------
/docs/presentation/theme/sixty-north-logo.small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/docs/presentation/theme/sixty-north-logo.small.png
--------------------------------------------------------------------------------
/docs/presentation/theme/sixty_north.css:
--------------------------------------------------------------------------------
1 | /**
2 | * The Sixty North reveal.js theme.
3 | *
4 | * Mostly just the "simple" theme with some modifications.
5 | */
6 | @import url('https://fonts.googleapis.com/css?family=Inconsolata|Roboto');
7 | /*********************************************
8 | * GLOBAL STYLES
9 | *********************************************/
10 | body {
11 | background: white;
12 | background-color: white;
13 | font-family: "Roboto", sans-serif;
14 | font-weight: normal;
15 | }
16 |
17 | .reveal {
18 | font-family: "Roboto", sans-serif;
19 | font-size: 36px;
20 | font-weight: normal;
21 | letter-spacing: -0.02em;
22 | color: black; }
23 |
24 | /* .rise-enabled { */
25 | /* background-color: white !important; */
26 | /* font-size: 125% !important; */
27 | /* background-image: url(sixty-north-device.png); */
28 | /* background-position: 99% 2%; */
29 | /* background-repeat: no-repeat; */
30 | /* background-origin: content-box; */
31 | /* padding: 5px 5px 5px 5px; */
32 | /* } */
33 |
34 |
35 | ::selection {
36 | color: white;
37 | background: rgba(0, 0, 0, 0.99);
38 | text-shadow: none; }
39 |
40 | /*********************************************
41 | * HEADERS
42 | *********************************************/
43 | .reveal h1,
44 | .reveal h2,
45 | .reveal h3,
46 | .reveal h4,
47 | .reveal h5,
48 | .reveal h6 {
49 | margin: 0 0 20px 0;
50 | color: black;
51 | line-height: 0.9em;
52 | letter-spacing: 0.02em;
53 | text-transform: none;
54 | text-shadow: none; }
55 |
56 | h2 {
57 | font-weight: 100;
58 | color: #888888;
59 | }
60 |
61 | .reveal h2 {
62 | font-weight: 100;
63 | color: #888888;
64 | }
65 |
66 | /*********************************************
67 | * LINKS
68 | *********************************************/
69 | .reveal a:not(.image) {
70 | color: darkblue;
71 | text-decoration: none;
72 | -webkit-transition: color .15s ease;
73 | -moz-transition: color .15s ease;
74 | -ms-transition: color .15s ease;
75 | -o-transition: color .15s ease;
76 | transition: color .15s ease; }
77 |
78 | .reveal a:not(.image):hover {
79 | color: #0000f1;
80 | text-shadow: none;
81 | border: none; }
82 |
83 | .reveal .roll span:after {
84 | color: #fff;
85 | background: #00003f; }
86 |
87 | /*********************************************
88 | * IMAGES
89 | *********************************************/
90 | .reveal section img {
91 | margin: 15px 0px;
92 | background: rgba(255, 255, 255, 0.12);
93 | border: 4px solid black;
94 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
95 | -webkit-transition: all .2s linear;
96 | -moz-transition: all .2s linear;
97 | -ms-transition: all .2s linear;
98 | -o-transition: all .2s linear;
99 | transition: all .2s linear; }
100 |
101 | .reveal a:hover img {
102 | background: rgba(255, 255, 255, 0.2);
103 | border-color: darkblue;
104 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
105 |
106 | /*********************************************
107 | * NAVIGATION CONTROLS
108 | *********************************************/
109 | .reveal .controls div.navigate-left,
110 | .reveal .controls div.navigate-left.enabled {
111 | border-right-color: darkblue; }
112 |
113 | .reveal .controls div.navigate-right,
114 | .reveal .controls div.navigate-right.enabled {
115 | border-left-color: darkblue; }
116 |
117 | .reveal .controls div.navigate-up,
118 | .reveal .controls div.navigate-up.enabled {
119 | border-bottom-color: darkblue; }
120 |
121 | .reveal .controls div.navigate-down,
122 | .reveal .controls div.navigate-down.enabled {
123 | border-top-color: darkblue; }
124 |
125 | .reveal .controls div.navigate-left.enabled:hover {
126 | border-right-color: #0000f1; }
127 |
128 | .reveal .controls div.navigate-right.enabled:hover {
129 | border-left-color: #0000f1; }
130 |
131 | .reveal .controls div.navigate-up.enabled:hover {
132 | border-bottom-color: #0000f1; }
133 |
134 | .reveal .controls div.navigate-down.enabled:hover {
135 | border-top-color: #0000f1; }
136 |
137 | /*********************************************
138 | * PROGRESS BAR
139 | *********************************************/
140 | .reveal .progress {
141 | background: rgba(0, 0, 0, 0.2); }
142 |
143 | .reveal .progress span {
144 | background: darkblue;
145 | -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
146 | -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
147 | -ms-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
148 | -o-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
149 | transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
150 |
151 | /*********************************************
152 | * SLIDE NUMBER
153 | *********************************************/
154 | .reveal .slide-number {
155 | color: darkblue; }
156 |
157 | .reveal section img {
158 | border: 0px solid black;
159 | box-shadow: none; }
160 |
161 | .reveal h1,
162 | .reveal h2,
163 | .reveal h3,
164 | .reveal h4,
165 | .reveal h5,
166 | .reveal h6 {
167 | word-wrap: normal;
168 | -webkit-hyphens: manual;
169 | -moz-hyphens: manual;
170 | hyphens: manual; }
171 |
172 | pre.src {
173 | background-color: #020202;
174 | color: #dddddd;
175 | }
176 |
177 | code {
178 | font-family: "Inconsolata", sans-serif;
179 | line-height: 1.4em;
180 | }
181 |
182 | .reveal .text_cell {
183 | font-size: 24px;
184 | }
185 |
186 | .reveal .code_cell {
187 | font-size: 24px;
188 | }
189 |
190 | .example {
191 | background-color: #FFFFCC; }
192 |
--------------------------------------------------------------------------------
/elm_kernel/__init__.py:
--------------------------------------------------------------------------------
1 | """A Jupyter kernel for Elm"""
2 |
3 | __version__ = '0.21.1'
4 |
5 | from .kernel import ElmKernel
6 |
--------------------------------------------------------------------------------
/elm_kernel/__main__.py:
--------------------------------------------------------------------------------
1 | from ipykernel.kernelapp import IPKernelApp
2 | from . import ElmKernel
3 |
4 | IPKernelApp.launch_instance(kernel_class=ElmKernel)
5 |
--------------------------------------------------------------------------------
/elm_kernel/install.py:
--------------------------------------------------------------------------------
1 | # Copied from the echo_kernel example:
2 | # https://github.com/jupyter/echo_kernel/blob/master/echo_kernel/install.py
3 | #!/usr/bin/python3
4 | import argparse
5 | import json
6 | import os
7 | import sys
8 |
9 | from jupyter_client.kernelspec import KernelSpecManager
10 | from IPython.utils.tempdir import TemporaryDirectory
11 |
12 | kernel_json = {
13 | "argv": [sys.executable, "-m", "elm_kernel", "-f", "{connection_file}"],
14 | "display_name": "Elm",
15 | "language": "elm",
16 | }
17 |
18 |
19 | def install_my_kernel_spec(user=True, prefix=None):
20 | with TemporaryDirectory() as td:
21 | os.chmod(td, 0o755) # Starts off as 700, not user readable
22 | with open(os.path.join(td, 'kernel.json'), 'w') as f:
23 | json.dump(kernel_json, f, sort_keys=True)
24 |
25 | print('Installing Jupyter kernel spec')
26 | ksm = KernelSpecManager()
27 | ksm.install_kernel_spec(
28 | td, 'elm', user=user, prefix=prefix)
29 |
30 | install_dir = ksm.get_kernel_spec(kernel_name="elm").resource_dir
31 |
32 | print("Installed to ", install_dir)
33 |
34 |
35 | def _is_root():
36 | try:
37 | return os.geteuid() == 0
38 | except AttributeError:
39 | return False # assume not an admin on non-Unix platforms
40 |
41 |
42 | def main(argv=None):
43 | ap = argparse.ArgumentParser()
44 | ap.add_argument(
45 | '--user', action='store_true',
46 | help="Install to the per-user kernels registry. Default if not root.")
47 | ap.add_argument(
48 | '--sys-prefix', action='store_true',
49 | help="Install to sys.prefix (e.g. a virtualenv or conda env)")
50 | ap.add_argument(
51 | '--prefix',
52 | help="Install to the given prefix. "
53 | "Kernelspec will be installed in {PREFIX}/share/jupyter/kernels/")
54 | args = ap.parse_args(argv)
55 |
56 | if args.sys_prefix:
57 | args.prefix = sys.prefix
58 | if not args.prefix and not _is_root():
59 | args.user = True
60 |
61 | if args.user:
62 | print("Elm Kernel will be Installed to per-user jupyter kernel registry.")
63 | else:
64 | print("Elm Kernel will be Installed to global jupyter kernel registry.")
65 |
66 | install_my_kernel_spec(user=args.user, prefix=args.prefix)
67 |
68 |
69 | if __name__ == '__main__':
70 | main(sys.argv[1:])
71 |
--------------------------------------------------------------------------------
/elm_kernel/kernel.py:
--------------------------------------------------------------------------------
1 | from collections import deque
2 | import contextlib
3 | import io
4 | from ipykernel.kernelbase import Kernel
5 | import os
6 | import shutil
7 | import subprocess
8 | import sys
9 | from tempfile import TemporaryDirectory
10 |
11 |
12 | class ElmKernel(Kernel):
13 | implementation = 'elm_kernel'
14 | implementation_version = '0.21.1'
15 | language = 'no-op'
16 | language_version = '0.19.1'
17 | language_info = {'name': 'elm',
18 | 'codemirror_mode': 'elm',
19 | 'mimetype': 'text/x-elm',
20 | 'file_extension': '.elm'}
21 | banner = "Display Elm output"
22 |
23 | def __init__(self, *args, **kwargs):
24 | super().__init__(*args, **kwargs)
25 | self._code = []
26 | self._tempdir = TemporaryDirectory()
27 |
28 |
29 | def do_shutdown(self, restart):
30 | self._tempdir.cleanup()
31 |
32 | def do_execute(self, code, silent,
33 | store_history=True,
34 | user_expressions=None,
35 | allow_stdin=False):
36 | self._code.append(code)
37 |
38 | if self._should_compile():
39 | try:
40 | code = "\n".join(self._code)
41 | self._code = []
42 | self._compile(code)
43 | except Exception as exc:
44 |
45 | self._send_error_result(str(exc))
46 | return {
47 | 'status': 'error',
48 | 'execution_count': self.execution_count,
49 | }
50 |
51 | return {
52 | 'status': 'ok',
53 | 'execution_count': self.execution_count,
54 | 'payload': [],
55 | 'user_expressions': {},
56 | }
57 |
58 | @contextlib.contextmanager
59 | def _temp_path(self, filename):
60 | """Yield `filename` inside the tempdir, but don't actually create the file.
61 | Then, on exit, delete the file if it exists.
62 | """
63 | try:
64 | path = os.path.join(self._tempdir.name, filename)
65 | yield path
66 | finally:
67 | with contextlib.suppress(OSError):
68 | if os.path.isfile(path) or os.path.islink(path):
69 | os.remove(path)
70 | else:
71 | shutil.rmtree(path, ignore_errors=True)
72 |
73 |
74 | def _elm_init(self):
75 | ''' Generate an init file in the temporary directory
76 | '''
77 | proc = subprocess.Popen(
78 | ['elm', 'init'],
79 | cwd=self._tempdir.name,
80 | stdin=subprocess.PIPE,
81 | stderr=subprocess.STDOUT,
82 | encoding=sys.getdefaultencoding())
83 |
84 | # respond to prompt in 'elm init'
85 | proc.communicate(input="y\n")
86 | proc.wait()
87 |
88 | def _elm_make(self, infile, outfile):
89 |
90 | subprocess.run(
91 | ['elm', 'make',
92 | infile, '--output={}'.format(outfile)],
93 | check=True,
94 | stdout=subprocess.PIPE,
95 | stderr=subprocess.STDOUT,
96 | cwd=self._tempdir.name,
97 | encoding=sys.getdefaultencoding())
98 |
99 |
100 | def _compile(self, code):
101 | self._link_build_environment()
102 |
103 | with self._temp_path('input.elm') as infile,\
104 | self._temp_path('index.js') as outfile,\
105 | self._temp_path('elm.json') as elm_json,\
106 | self._temp_path('src') as src_path:
107 |
108 | with open(infile, mode='wt') as f:
109 |
110 | f.write(code)
111 |
112 | try:
113 | # if elm.json doesn't exist yet, create it
114 | if not os.path.lexists(elm_json):
115 | self._elm_init()
116 | if not os.path.lexists(src_path):
117 | os.mkdir(src_path)
118 |
119 | self._elm_make(infile, outfile)
120 |
121 | with open(outfile, mode='rt') as f:
122 | javascript = f.read()
123 |
124 | self._send_success_result(javascript)
125 |
126 | except subprocess.CalledProcessError as err:
127 | # When compilation fails we send the compiler output to the
128 | # user but we don't count this as an error. A compiler error
129 | # might actually be the desired output of the cell.
130 | self._send_error_result(err.stdout)
131 |
132 | except Exception as err:
133 | self._send_error_result(repr(err))
134 | raise
135 |
136 |
137 | def _should_compile(self):
138 | assert self._code, "Should not be querying for compilation with no code!"
139 | lines = deque(io.StringIO(self._code[-1]), 1)
140 | return lines[0] == '-- compile-code' if lines else False
141 |
142 | def _send_error_result(self, msg):
143 | """Send an error message to the client.
144 |
145 | `msg` is the message to be sent to the client.
146 | """
147 | self.send_response(
148 | self.iopub_socket,
149 | 'display_data',
150 | {
151 | 'metadata': {},
152 | 'data': {
153 | 'text/html': '{} '.format(msg)
154 | }
155 | }
156 | )
157 |
158 | def _send_success_result(self, javascript):
159 | """Send messages to the client with the results of a successful compilation.
160 |
161 | `javascript` is the javascript generated by elm-make.
162 | """
163 | # TODO: pull module name from `code`
164 | module_name = "Main"
165 |
166 | div_id = 'elm-div-' + str(self.execution_count)
167 |
168 | template = """
169 | var defineElm = function(cb) {{
170 | if (this.Elm) {{
171 | this.oldElm = this.Elm;
172 | }}
173 | var define = null;
174 |
175 | {js}
176 |
177 | cb();
178 | }}
179 | ;
180 |
181 | var obj = new Object();
182 | defineElm.bind(obj)(function(){{
183 | var mountNode = document.getElementById('{div_id}');
184 | obj.Elm. {module_name}.init({{ node: mountNode }});
185 | }});
186 | """
187 |
188 | javascript = template.format(
189 | js=javascript,
190 | module_name=module_name,
191 | div_id=div_id)
192 |
193 | self.send_response(
194 | self.iopub_socket,
195 | 'display_data',
196 | {
197 | 'metadata': {},
198 | 'data': {
199 | 'text/html': '
'
200 | }
201 | }
202 | )
203 |
204 | self.send_response(
205 | self.iopub_socket,
206 | 'display_data',
207 | {
208 | 'metadata': {},
209 | 'data': {
210 | 'application/javascript': javascript
211 | }
212 | })
213 |
214 | def _link_build_environment(self):
215 | """Link elm.json and src to temporary directory where elm code is compiled
216 | """
217 | # existence of elm.json is not mandatory
218 | if os.path.isfile('elm.json'):
219 | # symlink requires abolute path
220 | os.symlink(
221 | os.path.join(os.getcwd(), 'elm.json'),
222 | os.path.join(self._tempdir.name, 'elm.json'))
223 |
224 | if os.path.isdir('src'):
225 | os.symlink(
226 | os.path.join(os.getcwd(), 'src'),
227 | os.path.join(self._tempdir.name, 'src'))
228 |
229 |
230 | if __name__ == '__main__':
231 | from ipykernel.kernelapp import IPKernelApp
232 | IPKernelApp.launch_instance(kernel_class=ElmKernel)
233 |
--------------------------------------------------------------------------------
/examples/hello-world.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Hello, world!\n",
8 | "This is the obligatory \"hello world\" for the Elm Jupyter kernel."
9 | ]
10 | },
11 | {
12 | "cell_type": "code",
13 | "execution_count": null,
14 | "metadata": {},
15 | "outputs": [],
16 | "source": [
17 | "import Html\n",
18 | "\n",
19 | "main = \n",
20 | " Html.text \"Hello, world!\"\n",
21 | " \n",
22 | "-- compile-code"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": null,
28 | "metadata": {},
29 | "outputs": [],
30 | "source": []
31 | }
32 | ],
33 | "metadata": {
34 | "kernelspec": {
35 | "display_name": "Elm",
36 | "language": "elm",
37 | "name": "elm"
38 | },
39 | "language_info": {
40 | "codemirror_mode": "elm",
41 | "file_extension": ".elm",
42 | "mimetype": "text/x-elm",
43 | "name": "elm"
44 | }
45 | },
46 | "nbformat": 4,
47 | "nbformat_minor": 2
48 | }
49 |
--------------------------------------------------------------------------------
/examples/images/tea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abingham/jupyter-elm-kernel/2973205caec5b68d0bf9eac87986063ffcdf3f77/examples/images/tea.png
--------------------------------------------------------------------------------
/examples/svg/SVG example.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Rendering SVGs in a notebook\n",
8 | "This shows how you can draw SVGs in notebooks using the `elm/svg` module.\n",
9 | "\n",
10 | "\n",
11 | "To do this, you need to make sure you have an `elm.json` in the same directory as the notebook. The `elm.json` needs to have a dependency on `elm/svg`, something like this:"
12 | ]
13 | },
14 | {
15 | "cell_type": "markdown",
16 | "metadata": {},
17 | "source": [
18 | "```json\n",
19 | "{\n",
20 | " \"type\": \"application\",\n",
21 | " \"source-directories\": [],\n",
22 | " \"elm-version\": \"0.19.0\",\n",
23 | " \"dependencies\": {\n",
24 | " \"direct\": {\n",
25 | " \"elm/browser\": \"1.0.1\",\n",
26 | " \"elm/core\": \"1.0.2\",\n",
27 | " \"elm/html\": \"1.0.0\",\n",
28 | " \"elm/svg\": \"1.0.1\"\n",
29 | " },\n",
30 | " \"indirect\": {\n",
31 | " \"elm/json\": \"1.1.3\",\n",
32 | " \"elm/time\": \"1.0.0\",\n",
33 | " \"elm/url\": \"1.0.0\",\n",
34 | " \"elm/virtual-dom\": \"1.0.2\"\n",
35 | " }\n",
36 | " },\n",
37 | " \"test-dependencies\": {\n",
38 | " \"direct\": {},\n",
39 | " \"indirect\": {}\n",
40 | " }\n",
41 | "}\n",
42 | "```"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "## Basic SVG example"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {
56 | "scrolled": false
57 | },
58 | "outputs": [],
59 | "source": [
60 | "import Html\n",
61 | "import Svg exposing (..)\n",
62 | "import Svg.Attributes exposing (..)\n",
63 | "\n",
64 | "main = \n",
65 | " svg [stroke \"#f00\"] [line [x1 \"0\", y1 \"1\", x2 \"100\", y2 \"100\"] []]\n",
66 | " \n",
67 | "-- compile-code"
68 | ]
69 | },
70 | {
71 | "cell_type": "markdown",
72 | "metadata": {},
73 | "source": [
74 | "## A more complex example"
75 | ]
76 | },
77 | {
78 | "cell_type": "code",
79 | "execution_count": null,
80 | "metadata": {
81 | "scrolled": false
82 | },
83 | "outputs": [],
84 | "source": [
85 | "import Svg exposing (..)\n",
86 | "import Svg.Attributes exposing (..)\n",
87 | "\n",
88 | "main =\n",
89 | " svg\n",
90 | " [ version \"1.1\", x \"0\", y \"0\", viewBox \"0 0 323.141 322.95\", height \"200px\", width \"200px\"\n",
91 | " ]\n",
92 | " [ polygon [ fill \"#F0AD00\", points \"161.649,152.782 231.514,82.916 91.783,82.916\" ] []\n",
93 | " , polygon [ fill \"#7FD13B\", points \"8.867,0 79.241,70.375 232.213,70.375 161.838,0\" ] []\n",
94 | " , rect\n",
95 | " [ fill \"#7FD13B\", x \"192.99\", y \"107.392\", width \"107.676\", height \"108.167\"\n",
96 | " , transform \"matrix(0.7071 0.7071 -0.7071 0.7071 186.4727 -127.2386)\"\n",
97 | " ]\n",
98 | " []\n",
99 | " , polygon [ fill \"#60B5CC\", points \"323.298,143.724 323.298,0 179.573,0\" ] []\n",
100 | " , polygon [ fill \"#5A6378\", points \"152.781,161.649 0,8.868 0,314.432\" ] []\n",
101 | " , polygon [ fill \"#F0AD00\", points \"255.522,246.655 323.298,314.432 323.298,178.879\" ] []\n",
102 | " , polygon [ fill \"#60B5CC\", points \"161.649,170.517 8.869,323.298 314.43,323.298\" ] []\n",
103 | " ]\n",
104 | " \n",
105 | "-- compile-code"
106 | ]
107 | }
108 | ],
109 | "metadata": {
110 | "kernelspec": {
111 | "display_name": "Elm",
112 | "language": "elm",
113 | "name": "elm"
114 | },
115 | "language_info": {
116 | "codemirror_mode": "elm",
117 | "file_extension": ".elm",
118 | "mimetype": "text/x-elm",
119 | "name": "elm"
120 | }
121 | },
122 | "nbformat": 4,
123 | "nbformat_minor": 2
124 | }
125 |
--------------------------------------------------------------------------------
/examples/svg/elm.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "source-directories": ["src"],
4 | "elm-version": "0.19.1",
5 | "dependencies": {
6 | "direct": {
7 | "elm/browser": "1.0.1",
8 | "elm/core": "1.0.2",
9 | "elm/html": "1.0.0",
10 | "elm/svg": "1.0.1"
11 | },
12 | "indirect": {
13 | "elm/json": "1.1.3",
14 | "elm/time": "1.0.0",
15 | "elm/url": "1.0.0",
16 | "elm/virtual-dom": "1.0.2"
17 | }
18 | },
19 | "test-dependencies": {
20 | "direct": {},
21 | "indirect": {}
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/the-elm-architecture.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# The Elm Architecture\n",
8 | "\n",
9 | "This is a pretty minimal example of The Elm Architecture in a Jupyter notebook.\n",
10 | "\n",
11 | ""
12 | ]
13 | },
14 | {
15 | "cell_type": "markdown",
16 | "metadata": {},
17 | "source": [
18 | "## Imports\n",
19 | "First, we have to import a few other modules"
20 | ]
21 | },
22 | {
23 | "cell_type": "code",
24 | "execution_count": null,
25 | "metadata": {},
26 | "outputs": [],
27 | "source": [
28 | "import Browser\n",
29 | "import Html exposing (..)\n",
30 | "import Html.Events exposing (..)"
31 | ]
32 | },
33 | {
34 | "cell_type": "markdown",
35 | "metadata": {},
36 | "source": [
37 | "## Messages\n",
38 | "Next we define our message \"vocabulary\". These are the various ways in which our model can be updated."
39 | ]
40 | },
41 | {
42 | "cell_type": "code",
43 | "execution_count": null,
44 | "metadata": {},
45 | "outputs": [],
46 | "source": [
47 | "type Msg = Inc | Dec"
48 | ]
49 | },
50 | {
51 | "cell_type": "markdown",
52 | "metadata": {},
53 | "source": [
54 | "## Model\n",
55 | "Our model is simply an integer in this case. While it's not strictly necessary to create an alias for it, we'll include one for completeness; for more complex models you'll almost always have an alias."
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "type alias Model = Int"
65 | ]
66 | },
67 | {
68 | "cell_type": "markdown",
69 | "metadata": {},
70 | "source": [
71 | "## Init and subscriptions\n",
72 | "Next we define our init and subscriptions. Init passes the starting value in to our main program.\n",
73 | "Subscriptions is for telling our main program what events we want to receive."
74 | ]
75 | },
76 | {
77 | "cell_type": "code",
78 | "execution_count": null,
79 | "metadata": {},
80 | "outputs": [],
81 | "source": [
82 | "init : () -> (Model, Cmd Msg)\n",
83 | "init _ = (0, Cmd.none)\n",
84 | "\n",
85 | "subscriptions : Model -> Sub Msg\n",
86 | "subscriptions _ = Sub.none"
87 | ]
88 | },
89 | {
90 | "cell_type": "markdown",
91 | "metadata": {},
92 | "source": [
93 | "## View\n",
94 | "The `view` function renders our model."
95 | ]
96 | },
97 | {
98 | "cell_type": "code",
99 | "execution_count": null,
100 | "metadata": {},
101 | "outputs": [],
102 | "source": [
103 | "view : Model -> Html Msg\n",
104 | "view model =\n",
105 | " div []\n",
106 | " [ button [ onClick Inc ] [ text \"+\" ]\n",
107 | " , div [] [ text (String.fromInt model) ]\n",
108 | " , button [ onClick Dec ] [ text \"-\" ]\n",
109 | " ]"
110 | ]
111 | },
112 | {
113 | "cell_type": "markdown",
114 | "metadata": {},
115 | "source": [
116 | "## Update\n",
117 | "The `update` function takes a `Msg` and a `Model`, returning a new, updated `Model`. "
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": null,
123 | "metadata": {},
124 | "outputs": [],
125 | "source": [
126 | "update : Msg -> Model -> (Model, Cmd Msg)\n",
127 | "update msg model =\n",
128 | " case msg of\n",
129 | " Inc -> (model + 1, Cmd.none)\n",
130 | " Dec -> (model - 1, Cmd.none)"
131 | ]
132 | },
133 | {
134 | "cell_type": "markdown",
135 | "metadata": {},
136 | "source": [
137 | "## Main\n",
138 | "Finally we tie everything together with `main`. "
139 | ]
140 | },
141 | {
142 | "cell_type": "code",
143 | "execution_count": null,
144 | "metadata": {},
145 | "outputs": [],
146 | "source": [
147 | "main =\n",
148 | " Browser.element \n",
149 | " { init = init\n",
150 | " , view = view\n",
151 | " , update = update\n",
152 | " , subscriptions = subscriptions\n",
153 | " }\n",
154 | " \n",
155 | "-- compile-code"
156 | ]
157 | }
158 | ],
159 | "metadata": {
160 | "kernelspec": {
161 | "display_name": "Elm",
162 | "language": "elm",
163 | "name": "elm"
164 | },
165 | "language_info": {
166 | "codemirror_mode": "elm",
167 | "file_extension": ".elm",
168 | "mimetype": "text/x-elm",
169 | "name": "elm"
170 | }
171 | },
172 | "nbformat": 4,
173 | "nbformat_minor": 2
174 | }
175 |
--------------------------------------------------------------------------------
/kernel.json:
--------------------------------------------------------------------------------
1 | {"argv":["python3","-m","elm_kernel", "-f", "{connection_file}"],
2 | "display_name":"Elm"
3 | }
4 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | with open('README.md') as f:
4 | readme = f.read()
5 |
6 | setup(
7 | name='elm_kernel',
8 | version='0.21.1',
9 | packages=['elm_kernel'],
10 | description='Jupyter kernel for executing Elm code',
11 | long_description=readme,
12 | author='Austin Bingham',
13 | author_email='austin@sixty-north.com',
14 | url='https://github.com/abingham/jupyter-elm-kernel',
15 | classifiers=[
16 | 'Intended Audience :: Developers',
17 | 'License :: OSI Approved :: MIT License',
18 | 'Programming Language :: Python :: 3',
19 | ],
20 | install_requires=['jupyter'],
21 | )
22 |
--------------------------------------------------------------------------------