├── .gitignore ├── LICENCE ├── README.md ├── assets ├── test-dnotes.gif └── test-notebook.json ├── package.json ├── src ├── frontend │ ├── .eslintrc.json │ ├── .gitignore │ ├── README.md │ ├── components │ │ ├── Cell │ │ │ └── index.tsx │ │ ├── CellEditor │ │ │ └── index.tsx │ │ ├── CellOutputRenderer │ │ │ └── index.tsx │ │ ├── Dashboard │ │ │ └── index.tsx │ │ ├── IconMenu │ │ │ ├── edit.tsx │ │ │ ├── file.tsx │ │ │ ├── run.tsx │ │ │ ├── server.tsx │ │ │ └── view.tsx │ │ ├── MenuBar │ │ │ ├── cellOptions.tsx │ │ │ ├── notebookOptionsBar.tsx │ │ │ └── saveStatusLabel.tsx │ │ ├── Modals │ │ │ └── Settings.tsx │ │ ├── NavBar │ │ │ └── index.tsx │ │ ├── Notebook │ │ │ └── index.tsx │ │ ├── NotebookTabPanel │ │ │ └── index.tsx │ │ ├── Panel │ │ │ ├── explorer.tsx │ │ │ ├── index.tsx │ │ │ └── search.tsx │ │ └── SideBar │ │ │ └── index.tsx │ ├── lib │ │ ├── FileSystem │ │ │ └── fileSystem.js │ │ ├── fixtures │ │ │ ├── editorThemes.ts │ │ │ └── languages.ts │ │ ├── helpers │ │ │ ├── createEmotionCache.ts │ │ │ └── utils.ts │ │ ├── interpreter │ │ │ └── server.ts │ │ ├── state │ │ │ ├── reducer.ts │ │ │ └── store.ts │ │ └── typings │ │ │ └── types.tsx │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── _document.js │ │ └── index.tsx │ ├── postcss.config.js │ ├── public │ │ ├── ace-builds-public-export │ │ │ ├── ace.js │ │ │ ├── ext-beautify.js │ │ │ ├── ext-code_lens.js │ │ │ ├── ext-elastic_tabstops_lite.js │ │ │ ├── ext-emmet.js │ │ │ ├── ext-error_marker.js │ │ │ ├── ext-hardwrap.js │ │ │ ├── ext-keybinding_menu.js │ │ │ ├── ext-language_tools.js │ │ │ ├── ext-linking.js │ │ │ ├── ext-modelist.js │ │ │ ├── ext-options.js │ │ │ ├── ext-prompt.js │ │ │ ├── ext-rtl.js │ │ │ ├── ext-searchbox.js │ │ │ ├── ext-settings_menu.js │ │ │ ├── ext-spellcheck.js │ │ │ ├── ext-split.js │ │ │ ├── ext-static_highlight.js │ │ │ ├── ext-statusbar.js │ │ │ ├── ext-textarea.js │ │ │ ├── ext-themelist.js │ │ │ ├── ext-whitespace.js │ │ │ ├── keybinding-emacs.js │ │ │ ├── keybinding-sublime.js │ │ │ ├── keybinding-vim.js │ │ │ ├── keybinding-vscode.js │ │ │ ├── mode-html.js │ │ │ ├── mode-javascript.js │ │ │ ├── mode-json.js │ │ │ ├── mode-markdown.js │ │ │ ├── mode-plain_text.js │ │ │ ├── mode-powershell.js │ │ │ ├── mode-sh.js │ │ │ ├── mode-tsx.js │ │ │ ├── mode-typescript.js │ │ │ ├── snippets │ │ │ │ ├── autohotkey.js │ │ │ │ ├── batchfile.js │ │ │ │ ├── c9search.js │ │ │ │ ├── c_cpp.js │ │ │ │ ├── cirru.js │ │ │ │ ├── d.js │ │ │ │ ├── diff.js │ │ │ │ ├── html.js │ │ │ │ ├── ini.js │ │ │ │ ├── io.js │ │ │ │ ├── javascript.js │ │ │ │ ├── json.js │ │ │ │ ├── jsx.js │ │ │ │ ├── markdown.js │ │ │ │ ├── mask.js │ │ │ │ ├── plain_text.js │ │ │ │ ├── powershell.js │ │ │ │ ├── sh.js │ │ │ │ ├── tsx.js │ │ │ │ └── typescript.js │ │ │ ├── theme-ambiance.js │ │ │ ├── theme-chaos.js │ │ │ ├── theme-chrome.js │ │ │ ├── theme-clouds.js │ │ │ ├── theme-clouds_midnight.js │ │ │ ├── theme-cobalt.js │ │ │ ├── theme-crimson_editor.js │ │ │ ├── theme-dawn.js │ │ │ ├── theme-dracula.js │ │ │ ├── theme-dreamweaver.js │ │ │ ├── theme-eclipse.js │ │ │ ├── theme-github.js │ │ │ ├── theme-gob.js │ │ │ ├── theme-gruvbox.js │ │ │ ├── theme-idle_fingers.js │ │ │ ├── theme-iplastic.js │ │ │ ├── theme-katzenmilch.js │ │ │ ├── theme-kr_theme.js │ │ │ ├── theme-kuroir.js │ │ │ ├── theme-merbivore.js │ │ │ ├── theme-merbivore_soft.js │ │ │ ├── theme-mono_industrial.js │ │ │ ├── theme-monokai.js │ │ │ ├── theme-nord_dark.js │ │ │ ├── theme-one_dark.js │ │ │ ├── theme-pastel_on_dark.js │ │ │ ├── theme-solarized_dark.js │ │ │ ├── theme-solarized_light.js │ │ │ ├── theme-sqlserver.js │ │ │ ├── theme-terminal.js │ │ │ ├── theme-textmate.js │ │ │ ├── theme-tomorrow.js │ │ │ ├── theme-tomorrow_night.js │ │ │ ├── theme-tomorrow_night_blue.js │ │ │ ├── theme-tomorrow_night_bright.js │ │ │ ├── theme-tomorrow_night_eighties.js │ │ │ ├── theme-twilight.js │ │ │ ├── theme-vibrant_ink.js │ │ │ ├── theme-xcode.js │ │ │ ├── worker-base.js │ │ │ ├── worker-coffee.js │ │ │ ├── worker-css.js │ │ │ ├── worker-html.js │ │ │ ├── worker-javascript.js │ │ │ ├── worker-json.js │ │ │ ├── worker-lua.js │ │ │ ├── worker-php.js │ │ │ ├── worker-xml.js │ │ │ └── worker-xquery.js │ │ ├── favicon.ico │ │ └── vercel.svg │ ├── styles │ │ ├── globals.css │ │ └── themes │ │ │ ├── darkTheme.js │ │ │ └── lightTheme.js │ ├── tailwind.config.js │ ├── tsconfig.json │ └── yarn.lock └── server │ ├── .babelrc │ ├── .coveralls.yml │ ├── .eslintrc.json │ ├── .gitignore │ ├── .npmignore │ ├── .prettierrc │ ├── babel.config.json │ ├── package.json │ ├── src │ ├── config │ │ └── env.ts │ ├── index.ts │ ├── routes │ │ └── code_routes.ts │ ├── runner │ │ └── nodejs.ts │ ├── types.ts │ └── utils │ │ └── index.ts │ ├── tests │ └── index.test.ts │ ├── tsconfig.json │ ├── yarn-error.log │ └── yarn.lock └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | src/frontend/.env 2 | src/server/.env 3 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2020] [Dnotebook] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # DNOTEBOOK 3 | **Dnotebook** allows you to create and share pages that contain live code, text and visualizations in a textbook-like manner. 4 | 5 | 6 | > We are redesigning Dnotebook to feel more native. Track progress in this epic [issue](https://github.com/javascriptdata/dnotebook/issues/10), [project board](https://github.com/javascriptdata/dnotebook/projects/1). 7 | 8 | ### Features: 9 | - Easily perform interactive data exploration and analysis using efficient JavaScript packages like Danfo.js. 10 | - Easily build, train and prototype machine learning models using different tools like Tensorflow.js 11 | - Learn JavaScript in an interactive/visual style. This can hasten learning and understanding. 12 | - Plain Experimentation/Prototyping. Any experimentation performed in JavaScript can run on Dnotebooks. 13 | ## Tech stack 14 | * [Next.js](https://nextjs.org/) 15 | * React 16 | * [Material UI](https://mui.com/) 17 | * [Tailwind CSS](https://tailwindcss.com/) 18 | * Express 19 | 20 | ## Developers 21 | 22 | * Clone the repo 23 | ```sh 24 | git clone https://github.com/javascriptdata/dnotebook.git 25 | cd dnotebook 26 | git checkout ref/typescript 27 | ``` 28 | 29 | * Install packages with `yarn`. `yarn` is our preferred package manager 30 | 31 | ```sh 32 | yarn install 33 | ``` 34 | * Run the app in dev mode 35 | 36 | ```sh 37 | yarn dev 38 | ``` 39 | 40 | * Open your browser and navigate to `http://localhost:3000/` 41 | 42 | 43 | ### Discussion and Development 44 | Development discussions take place on our [issues](https://github.com/javascriptdata/dnotebook/issues/10) tab. 45 | 46 | ### Contributing to Dnotebook 47 | All contributions, bug reports, bug fixes, documentation improvements, enhancements, and ideas are welcome. 48 | 49 | #### Licence [MIT](https://github.com/opensource9ja/dnotebook/blob/master/LICENCE) 50 | 51 | 52 | -------------------------------------------------------------------------------- /assets/test-dnotes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/javascriptdata/dnotebook/831e752c2b4f635f985d4b1eab76a69fa5fdf4b9/assets/test-dnotes.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dnotebook", 3 | "version": "1.0.0", 4 | "description": "Jupyter-like library for JavaScript environment. It allows you to create and share pages that contain live code, text and visualizations.", 5 | "main": "dnotebook/src/bin/www", 6 | "contributors": [ 7 | { 8 | "name": "Rising Odegua" 9 | } 10 | ], 11 | "files": [ 12 | "dnotebook/" 13 | ], 14 | "scripts": { 15 | "install": "cd src/frontend && yarn && cd ../server && yarn", 16 | "build": "cd src/frontend && yarn build && cd ../server && yarn build", 17 | "dev": "cd src/frontend && yarn dev && cd ../server && yarn dev", 18 | "start": "cd src/server && yarn && yarn start & cd src/frontend && yarn && yarn build && yarn start" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/opensource9ja/dnotebook.git" 23 | }, 24 | "keywords": [ 25 | "Notebook", 26 | "Interactive-analysis", 27 | "live-code", 28 | "Jupyter-Notebook", 29 | "Danfojs" 30 | ], 31 | "author": "Rising Odegua ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/opensource9ja/dnotebook/issues" 35 | }, 36 | "homepage": "https://github.com/opensource9ja/dnotebook#readme" 37 | } -------------------------------------------------------------------------------- /src/frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /src/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules/* 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | 15 | # production 16 | build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /src/frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /src/frontend/components/CellEditor/index.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from "next/dynamic"; 2 | import { useState } from "react"; 3 | import { useDispatch, useSelector } from "react-redux"; 4 | import { updateCells, setNotebookSavingStatus } from "../../lib/state/reducer" 5 | import { AppState, NbCell } from "../../lib/typings/types"; 6 | 7 | const AceEditor = dynamic( 8 | async () => { 9 | const editor = await import('react-ace'); 10 | const ace = await import('ace-builds/src-noconflict/ace'); 11 | /** @ts-ignore */ 12 | import('brace/ext/language_tools'); //Add support for auto complete and snippets 13 | ace.config.set("basePath", "/ace-builds-public-export"); 14 | return editor; 15 | }, 16 | { 17 | loading: () => ( 18 |
19 | ), 20 | ssr: false, 21 | }, 22 | ); 23 | 24 | 25 | const Editor = ({ cell }: { cell: NbCell }) => { 26 | const dispatch = useDispatch(); 27 | 28 | const { notebooks, activeNotebookName, config } = useSelector((state: { app: AppState }) => state.app) 29 | const notebook = notebooks[activeNotebookName] 30 | const { cells } = notebook 31 | 32 | const [code, updateCode] = useState(cell?.content); 33 | 34 | const handleCodeChange = (newCode: any) => { 35 | dispatch(setNotebookSavingStatus("unsaved")) 36 | updateCode(newCode); 37 | const newCurrCell = { ...cell, content: newCode } 38 | 39 | const newCells = { ...cells, [cell.id]: newCurrCell } 40 | dispatch(updateCells({ newCells, activeNotebookName })) 41 | } 42 | 43 | return ( 44 | 66 | ); 67 | }; 68 | 69 | export default Editor; -------------------------------------------------------------------------------- /src/frontend/components/CellOutputRenderer/index.tsx: -------------------------------------------------------------------------------- 1 | import { NbCell } from "../../lib/typings/types"; 2 | 3 | 4 | const cellOutputRenderer = ({ cell }: { cell: NbCell }) => { 5 | return ( 6 |
7 | {cell.outputError !== "" ? ( 8 |
9 | ) : ( 10 |
11 |
12 |
13 | )} 14 |
15 | ) 16 | } 17 | 18 | export default cellOutputRenderer; -------------------------------------------------------------------------------- /src/frontend/components/Dashboard/index.tsx: -------------------------------------------------------------------------------- 1 | 2 | import Typography from '@mui/material/Typography'; 3 | 4 | export default function Dashboard() { 5 | return ( 6 |
7 |
8 | 9 | Welcome to the Dashboard! 10 | Take a look at your notebooks and start coding! 11 | 12 |
13 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/frontend/components/IconMenu/run.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Paper from '@mui/material/Paper'; 3 | import MenuList from '@mui/material/MenuList'; 4 | import MenuItem from '@mui/material/MenuItem'; 5 | import ListItemText from '@mui/material/ListItemText'; 6 | import ListItemIcon from '@mui/material/ListItemIcon'; 7 | import Typography from '@mui/material/Typography'; 8 | import PlayArrowIcon from '@mui/icons-material/PlayArrow'; 9 | import PlaylistPlayIcon from '@mui/icons-material/PlaylistPlay'; 10 | import PlayLessonIcon from '@mui/icons-material/PlayLesson'; 11 | 12 | export default function RunMenu() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | Run Cell 21 | 22 | ⇧ Enter 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Run Cell and Insert Below 31 | 32 | Ctrl Enter 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Run All Cell 41 | 42 | 43 | 44 | 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /src/frontend/components/IconMenu/server.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Divider from '@mui/material/Divider'; 3 | import Paper from '@mui/material/Paper'; 4 | import MenuList from '@mui/material/MenuList'; 5 | import MenuItem from '@mui/material/MenuItem'; 6 | import ListItemText from '@mui/material/ListItemText'; 7 | import ListItemIcon from '@mui/material/ListItemIcon'; 8 | import Typography from '@mui/material/Typography'; 9 | import ContentCut from '@mui/icons-material/ContentCut'; 10 | import ContentCopy from '@mui/icons-material/ContentCopy'; 11 | import ContentPaste from '@mui/icons-material/ContentPaste'; 12 | import UndoIcon from '@mui/icons-material/Undo'; 13 | import RedoIcon from '@mui/icons-material/Redo'; 14 | import ContentPasteIcon from '@mui/icons-material/ContentPaste'; 15 | import DeleteIcon from '@mui/icons-material/Delete'; 16 | import SelectAllIcon from '@mui/icons-material/SelectAll'; 17 | import JoinFullIcon from '@mui/icons-material/JoinFull'; 18 | import ClearIcon from '@mui/icons-material/Clear'; 19 | import ClearAllIcon from '@mui/icons-material/ClearAll'; 20 | import CancelScheduleSendIcon from '@mui/icons-material/CancelScheduleSend'; 21 | import RestartAltIcon from '@mui/icons-material/RestartAlt'; 22 | import PlaylistPlayIcon from '@mui/icons-material/PlaylistPlay'; 23 | import PlaylistRemoveIcon from '@mui/icons-material/PlaylistRemove'; 24 | import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew'; 25 | 26 | export default function ServerMenu() { 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Interrupt Server 36 | 37 | I, I 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Restart Server 47 | 48 | 49 | 50 | 51 | 52 | 53 | Restart Server and Clear All Outputs 54 | 55 | 56 | 57 | 58 | 59 | 60 | Restart Server and Run All Cells 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Shut Down Server 69 | 70 | 71 | 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /src/frontend/components/MenuBar/saveStatusLabel.tsx: -------------------------------------------------------------------------------- 1 | import { NotebookSaveStatus } from "../../lib/typings/types"; 2 | import { Chip } from "@mui/material"; 3 | import CheckIcon from '@mui/icons-material/Check'; 4 | import { FileDownload } from "@mui/icons-material"; 5 | 6 | export default function SaveStatusLabel({ notebookStatus }: { notebookStatus: NotebookSaveStatus }) { 7 | switch (notebookStatus) { 8 | case "saved": 9 | return } 14 | /> 15 | case "saving": 16 | return 21 | case "unsaved": 22 | return 27 | case "error": 28 | return 33 | case "downloading": 34 | return } 39 | /> 40 | case "downloaded": 41 | return } 46 | /> 47 | default: 48 | return
49 | } 50 | } -------------------------------------------------------------------------------- /src/frontend/components/Notebook/index.tsx: -------------------------------------------------------------------------------- 1 | import NoteBookCell from '../Cell'; 2 | import { useSelector } from "react-redux"; 3 | import { AppState, NbCell } from '../../lib/typings/types'; 4 | import Dashboard from '../Dashboard'; 5 | 6 | const NotebookTab = () => { 7 | const { notebooks, activeNotebookName } = useSelector((state: { app: AppState }) => state.app) 8 | const notebook = notebooks[activeNotebookName] 9 | const { cellIds, cells } = notebook 10 | return ( 11 |
12 | { 13 | notebook.name === "Dashboard" ? ( 14 | 15 | ) : ( 16 | cellIds.map((cellId: string, i: number) => { 17 | const cell: NbCell = cells[cellId] 18 | return
19 | }) 20 | ) 21 | } 22 |
23 | ) 24 | } 25 | 26 | export default NotebookTab 27 | -------------------------------------------------------------------------------- /src/frontend/components/Panel/explorer.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@mui/material"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import React, { useState } from "react"; 4 | import { setDirectories } from "../../lib/state/reducer"; 5 | import { openFolder, openNotebookFromFileName } from "../../lib/FileSystem/fileSystem"; 6 | import { connect } from "react-redux"; 7 | import TreeView from "@mui/lab/TreeView"; 8 | import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; 9 | import ChevronRightIcon from "@mui/icons-material/ChevronRight"; 10 | import TreeItem from "@mui/lab/TreeItem"; 11 | import { 12 | setActiveWorkspaceDirectoryHandle, 13 | setActiveNotebookTabNumber, 14 | updateActiveNotebookName, 15 | addNotebook, 16 | } from "../../lib/state/reducer" 17 | import { AppState } from "../../lib/typings/types"; 18 | interface PanelProps { 19 | state: any; 20 | name: string; 21 | items: any, 22 | }; 23 | 24 | const RecursiveComponent: React.FC = ({ name, items }) => { 25 | const hasChildren = items && items; 26 | 27 | return ( 28 | <> 29 | 30 | {hasChildren && 31 | items.map((item: JSX.IntrinsicAttributes & PanelProps & { children?: React.ReactNode; }) => )} 32 | 33 | 34 | ); 35 | }; 36 | 37 | export const Explorer: React.FC<{ state: any }> = ({ state }) => { 38 | const dispatch = useDispatch(); 39 | const [selected, setSelected] = useState([]); 40 | const { activeWorkspaceDirectoryHandle, notebooks, activeNotebookName } = useSelector((state: { app: AppState }) => state.app) 41 | 42 | async function onFolderSelect() { 43 | const folders = await openFolder(); 44 | dispatch(setActiveWorkspaceDirectoryHandle(folders?.directoryHandle)) 45 | dispatch(setDirectories(folders)); 46 | } 47 | 48 | const handleSelect = async (event: React.SyntheticEvent, nodeIds: string[]) => { 49 | setSelected(nodeIds); 50 | //@ts-ignore 51 | const selectedItemName = event.target.innerText 52 | if (!selectedItemName){ 53 | return; 54 | } 55 | 56 | if (["dnb", "md", "txt", "js", "ts", "sh", "json"].includes(selectedItemName.split(".").pop())) { 57 | 58 | if (selectedItemName === activeNotebookName){ 59 | return; 60 | } 61 | 62 | if (notebooks[selectedItemName]){ 63 | const tabNames = Object.keys(notebooks) 64 | const tabIndex = tabNames.indexOf(selectedItemName) 65 | dispatch(setActiveNotebookTabNumber(tabIndex)) 66 | dispatch(updateActiveNotebookName(selectedItemName)) 67 | return; 68 | } 69 | 70 | 71 | const notebook = await openNotebookFromFileName(selectedItemName, activeWorkspaceDirectoryHandle) 72 | dispatch(addNotebook(notebook)) 73 | const tabNames = Object.keys(notebooks) 74 | dispatch(setActiveNotebookTabNumber(tabNames.length)) 75 | dispatch(updateActiveNotebookName(selectedItemName)) 76 | } 77 | }; 78 | 79 | 80 | return ( 81 |
82 | {state && Object.keys(state).length ? ( 83 | } 86 | defaultExpandIcon={} 87 | selected={selected} 88 | onNodeSelect={handleSelect} 89 | sx={{ flexGrow: 1, maxWidth: 400, overflowY: "auto" }} 90 | > 91 | 92 | 93 | ) : ( 94 |
95 |

You have not yet added a folder to the workspace.

96 | 99 |
100 | )} 101 |
102 | ); 103 | }; 104 | 105 | interface State { 106 | state: Object; 107 | app: any; 108 | } 109 | function mapStateToProps(state: State) { 110 | return { 111 | state: state.app.directories, 112 | }; 113 | } 114 | export default connect(mapStateToProps, null)(Explorer); 115 | -------------------------------------------------------------------------------- /src/frontend/components/Panel/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Explorer from "./explorer"; 3 | import Search from "./search"; 4 | import RefreshIcon from "@mui/icons-material/Refresh"; 5 | import NoteAddIcon from '@mui/icons-material/NoteAdd'; 6 | import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder'; 7 | import { Typography } from '@mui/material'; 8 | import Button from '@mui/material/Button'; 9 | import { refreshWorkspaceDirectory } from "../../lib/FileSystem/fileSystem"; 10 | import { useSelector, useDispatch } from 'react-redux'; 11 | import { AppState } from '../../lib/typings/types'; 12 | import { setDirectories } from "../../lib/state/reducer"; 13 | 14 | interface PanelProps { 15 | showPanel: String | null; 16 | } 17 | 18 | 19 | export const Panel: React.FC = ({ showPanel }) => { 20 | const dispatch = useDispatch() 21 | const { activeWorkspaceDirectoryHandle } = useSelector((state: { app: AppState }) => state.app) 22 | 23 | 24 | const handleWorkspaceDirectoryRefresh = async () => { 25 | const folders = await refreshWorkspaceDirectory(activeWorkspaceDirectoryHandle); 26 | dispatch(setDirectories(folders)); 27 | } 28 | 29 | return ( 30 |
31 |
32 | 33 | {showPanel} 34 | 35 | { 36 | showPanel == "Explorer" ? ( 37 |
38 | 41 | 44 | 47 |
48 | ) : ( 49 | "" 50 | ) 51 | } 52 |
53 | {showPanel && showPanel === "Explorer" && } 54 | {showPanel && showPanel === "Search" && } 55 |
56 | ); 57 | } 58 | 59 | 60 | export default Panel; -------------------------------------------------------------------------------- /src/frontend/components/Panel/search.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function Search() { 4 | return
hello
; 5 | } 6 | 7 | export default Search; 8 | -------------------------------------------------------------------------------- /src/frontend/components/SideBar/index.tsx: -------------------------------------------------------------------------------- 1 | import FolderRoundedIcon from "@mui/icons-material/FolderRounded"; 2 | import SearchRoundedIcon from "@mui/icons-material/SearchRounded"; 3 | import { Button } from "@mui/material"; 4 | 5 | export const sideNavData = [ 6 | { 7 | name: "Explorer", 8 | icon: , 9 | }, 10 | { 11 | name: "Search", 12 | icon: , 13 | }, 14 | ]; 15 | 16 | interface MiniDrawerProps { 17 | setShowPanel: Function; 18 | showPanel: String | null; 19 | } 20 | export const MiniDrawer: React.FC = ({setShowPanel, showPanel}) => { 21 | return ( 22 |
23 |
24 | {sideNavData.map((data) => { 25 | return ( 26 |
33 | 47 |
48 | ); 49 | })} 50 |
51 |
52 | ); 53 | } 54 | 55 | export default MiniDrawer; -------------------------------------------------------------------------------- /src/frontend/lib/fixtures/editorThemes.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export const editorThemes = ["monokai", "github", "tomorrow", "kuroir", "twilight", "xcode", "textmate"]; -------------------------------------------------------------------------------- /src/frontend/lib/fixtures/languages.ts: -------------------------------------------------------------------------------- 1 | import { CellLanguages } from "../typings/types"; 2 | 3 | 4 | const cellLanguages: CellLanguages = { 5 | "typescript": { 6 | value: "typescript", 7 | name: "TypeScript", 8 | extensions: [".ts", ".tsx"], 9 | }, 10 | "javascript": { 11 | value: "javascript", 12 | name: "JavaScript", 13 | extensions: [".js", ".jsx"], 14 | }, 15 | // "json": { 16 | // value: "json", 17 | // name: "JSON", 18 | // extensions: [".json"], 19 | // }, 20 | "markdown": { 21 | value: "markdown", 22 | name: "Markdown", 23 | extensions: [".md", ".markdown"], 24 | }, 25 | "html": { 26 | value: "html", 27 | name: "HTML", 28 | extensions: [".html", ".htm"], 29 | }, 30 | "bash": { 31 | value: "sh", 32 | name: "Bash", 33 | extensions: [".sh", ".bash"], 34 | }, 35 | } 36 | 37 | export { 38 | cellLanguages, 39 | } -------------------------------------------------------------------------------- /src/frontend/lib/helpers/createEmotionCache.ts: -------------------------------------------------------------------------------- 1 | import createCache from '@emotion/cache'; 2 | 3 | const createEmotionCache = () => { 4 | return createCache({ key: 'css' }); 5 | }; 6 | 7 | export default createEmotionCache; -------------------------------------------------------------------------------- /src/frontend/lib/helpers/utils.ts: -------------------------------------------------------------------------------- 1 | import { outputError } from "../typings/types"; 2 | 3 | export const formatErrorMessage = (err: Error): outputError => { 4 | return { output: err.message, name: err.name, __$hasError: true } 5 | } 6 | 7 | export const cleanErrorMessage = (err: outputError) => { 8 | const errorMessage = err.output.split('\n')[0] 9 | const errorName = err.name 10 | const fullErrorMessage = errorName + ': ' + errorMessage 11 | return fullErrorMessage 12 | } -------------------------------------------------------------------------------- /src/frontend/lib/state/reducer.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { AppState } from "../typings/types"; 3 | import { v4 as uuid_v4 } from "uuid"; 4 | 5 | const initialState: AppState = { 6 | interpreterMode: "node", 7 | activeNotebookName: "Dashboard", 8 | activeNotebookTabNumber: 0, 9 | notebooks: { 10 | "Dashboard": { //Default open tab for Dashboard 11 | notebookId: uuid_v4(), 12 | name: "Dashboard", 13 | cellIds: [], 14 | cells: {}, 15 | }, 16 | }, 17 | config: { 18 | cellTheme: "monokai", 19 | cellFontSize: 14, 20 | cellEnableBasicAutocompletion: true, 21 | cellEnableLiveAutocompletion: true, 22 | cellEnableSnippets: true, 23 | cellShowLineNumbers: false, 24 | cellTabSize: 2, 25 | width: "100%", 26 | notebookThemeMode: "light", 27 | autosaveNotebook: true, 28 | }, 29 | directories: [], 30 | notebookSavingStatus: "saved", 31 | activeWorkspaceDirectoryHandle: null, 32 | } 33 | 34 | const appReducer = createSlice({ 35 | name: "app", 36 | initialState: { ...initialState }, 37 | reducers: { 38 | setInterpreterMode: (state, action) => { 39 | state.interpreterMode = action.payload; 40 | }, 41 | updateCellIds: (state, action) => { 42 | const { newCellIds, activeNotebookName } = action.payload; 43 | state.notebooks[activeNotebookName].cellIds = newCellIds; 44 | }, 45 | updateCells: (state, action) => { 46 | const { newCells, activeNotebookName } = action.payload; 47 | state.notebooks[activeNotebookName].cells = newCells; 48 | }, 49 | updateConfig: (state, action) => { 50 | state.config = action.payload; 51 | }, 52 | updateActiveNotebookName: (state, action) => { 53 | state.activeNotebookName = action.payload; 54 | }, 55 | setDirectories: (state, action) => { 56 | state.directories = action.payload; 57 | }, 58 | updateNotebooks: (state, action) => { 59 | state.notebooks = action.payload; 60 | }, 61 | addNotebook: (state, action) => { 62 | const notebook = action.payload; 63 | state.notebooks[notebook.name] = notebook; 64 | }, 65 | setActiveNotebookTabNumber: (state, action) => { 66 | state.activeNotebookTabNumber = action.payload; 67 | }, 68 | setNotebookSavingStatus: (state, action) => { 69 | state.notebookSavingStatus = action.payload; 70 | }, 71 | setActiveWorkspaceDirectoryHandle: (state, action) => { 72 | state.activeWorkspaceDirectoryHandle = action.payload; 73 | } 74 | } 75 | }); 76 | 77 | export const { 78 | setInterpreterMode, 79 | updateCellIds, 80 | updateCells, 81 | updateConfig, 82 | updateActiveNotebookName, 83 | setDirectories, 84 | addNotebook, 85 | setActiveNotebookTabNumber, 86 | setNotebookSavingStatus, 87 | updateNotebooks, 88 | setActiveWorkspaceDirectoryHandle, 89 | } = appReducer.actions; 90 | 91 | export default appReducer.reducer; -------------------------------------------------------------------------------- /src/frontend/lib/state/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit"; 2 | import appReducer from "./reducer"; 3 | 4 | const store = configureStore({ 5 | devTools: true, 6 | reducer: { 7 | app: appReducer, 8 | }, 9 | middleware: (getDefaultMiddleware) => 10 | getDefaultMiddleware({ 11 | serializableCheck: false, 12 | }), 13 | }); 14 | 15 | export default store; -------------------------------------------------------------------------------- /src/frontend/lib/typings/types.tsx: -------------------------------------------------------------------------------- 1 | export type outputError = { 2 | name: string, 3 | output: string, 4 | __$hasError?: boolean, 5 | } 6 | 7 | export type InterpreterInput = { 8 | content: string; 9 | language: string; 10 | callback: (accumulatedResult: string | outputError, hasErrors: boolean) => void; 11 | } 12 | 13 | export type NbCell = { 14 | id: string; 15 | mode: LangaugeOption; 16 | content: string; 17 | output: any; 18 | outputError?: any; 19 | } 20 | 21 | export type DirectoryObj = { 22 | key: string, 23 | value: Object, 24 | } 25 | 26 | export type Notebook = { 27 | notebookId: string; 28 | name: string; 29 | cellIds: string[]; 30 | cells: { 31 | [key: string]: NbCell 32 | }, 33 | metadata?: { 34 | [key: string]: any 35 | }, 36 | } 37 | 38 | export type NotebookConfig = { 39 | cellTheme: string, 40 | cellFontSize: number, 41 | cellEnableBasicAutocompletion: boolean, 42 | cellEnableLiveAutocompletion: boolean, 43 | cellEnableSnippets: boolean, 44 | cellShowLineNumbers: boolean, 45 | cellTabSize: number, 46 | width: string, 47 | height: string, 48 | notebookThemeMode: string, 49 | autosaveNotebook: boolean, 50 | } 51 | 52 | export type LangaugeOption = "typescript" | "javascript" | "sh" | "html" | "markdown" 53 | 54 | export type CellLanguages = { 55 | [id: string]: { 56 | value: string; 57 | name: string; 58 | extensions: string[]; 59 | } 60 | } 61 | 62 | export type NotebookSaveStatus = "unsaved" | "saving" | "saved" | "error" | "downloading" | "downloaded" 63 | 64 | export type AppState = { 65 | interpreterMode: string; 66 | activeNotebookName: string; 67 | activeNotebookTabNumber: number; 68 | notebooks: { 69 | [key: string]: Notebook 70 | } 71 | directories: DirectoryObj[], 72 | config: Partial; 73 | notebookSavingStatus: NotebookSaveStatus, 74 | activeWorkspaceDirectoryHandle: any 75 | } -------------------------------------------------------------------------------- /src/frontend/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /src/frontend/next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | env: { 4 | NEXT_PUBLIC_CODE_SERVER_URL: "http://localhost:8383/api/v1/code", 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dnotebook-ui", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next dev", 6 | "build": "next build", 7 | "start": "next start", 8 | "lint": "next lint" 9 | }, 10 | "dependencies": { 11 | "@emotion/cache": "^11.7.1", 12 | "@emotion/react": "^11.7.1", 13 | "@emotion/server": "^11.4.0", 14 | "@emotion/styled": "^11.6.0", 15 | "@fontsource/roboto": "^4.5.1", 16 | "@mui/icons-material": "^5.3.0", 17 | "@mui/lab": "^5.0.0-alpha.65", 18 | "@mui/material": "^5.3.0", 19 | "@reduxjs/toolkit": "^1.7.1", 20 | "ace-builds": "^1.4.13", 21 | "autoprefixer": "^10.4.0", 22 | "axios": "^0.24.0", 23 | "brace": "^0.11.1", 24 | "marked": "^4.0.10", 25 | "next": "12.0.3", 26 | "postcss": "^8.3.11", 27 | "react": "17.0.2", 28 | "react-ace": "^9.5.0", 29 | "react-dom": "17.0.2", 30 | "react-redux": "^7.2.6", 31 | "react-spring": "^9.4.2", 32 | "tailwindcss": "^2.2.19", 33 | "uuid": "^8.3.2" 34 | }, 35 | "devDependencies": { 36 | "@types/marked": "^4.0.1", 37 | "@types/node": "16.11.7", 38 | "@types/react": "17.0.34", 39 | "@types/uuid": "^8.3.4", 40 | "eslint": "7", 41 | "eslint-config-next": "12.0.3", 42 | "typescript": "4.4.4" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/frontend/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | import store from '../lib/state/store' 3 | import { Provider } from 'react-redux' 4 | import { CacheProvider } from '@emotion/react'; 5 | import createEmotionCache from '../lib/helpers/createEmotionCache'; 6 | 7 | const clientSideEmotionCache = createEmotionCache(); 8 | 9 | 10 | function MyApp(props: any) { 11 | const { Component, emotionCache = clientSideEmotionCache, pageProps } = props; 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | 21 | export default MyApp 22 | -------------------------------------------------------------------------------- /src/frontend/pages/_document.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Document, { Html, Head, Main, NextScript } from 'next/document'; 3 | import createEmotionServer from '@emotion/server/create-instance'; 4 | import createEmotionCache from '../lib/helpers/createEmotionCache'; 5 | 6 | export default class MyDocument extends Document { 7 | render() { 8 | return ( 9 | 10 | 11 | 15 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | ); 26 | } 27 | } 28 | 29 | // `getInitialProps` belongs to `_document` (instead of `_app`), 30 | // it's compatible with static-site generation (SSG). 31 | MyDocument.getInitialProps = async (ctx) => { 32 | 33 | const originalRenderPage = ctx.renderPage; 34 | 35 | // You can consider sharing the same emotion cache between all the SSR requests to speed up performance. 36 | // However, be aware that it can have global side effects. 37 | const cache = createEmotionCache(); 38 | const { extractCriticalToChunks } = createEmotionServer(cache); 39 | 40 | /* eslint-disable */ 41 | ctx.renderPage = () => 42 | originalRenderPage({ 43 | enhanceApp: (App) => 44 | function EnhanceApp(props) { 45 | return ; 46 | }, 47 | }); 48 | /* eslint-enable */ 49 | 50 | const initialProps = await Document.getInitialProps(ctx); 51 | // This is important. It prevents emotion to render invalid HTML. 52 | // See https://github.com/mui-org/material-ui/issues/26561#issuecomment-855286153 53 | const emotionStyles = extractCriticalToChunks(initialProps.html); 54 | const emotionStyleTags = emotionStyles.styles.map((style) => ( 55 |