├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── DOC.md ├── LICENSE ├── package-lock.json ├── package.json ├── readme.md ├── src ├── css │ ├── accordion.css │ ├── cascaded-menu.css │ ├── console.css │ ├── construct-doc.css │ ├── cursor.css │ ├── doc-box.css │ ├── draft-mode.css │ ├── editor.css │ ├── hole.css │ ├── index.css │ ├── messages.css │ ├── notification.css │ ├── suggestion-menu.css │ ├── toolbox.css │ └── tooltip.css ├── docs │ ├── add-var.json │ ├── add.json │ ├── and.json │ ├── assign-add.json │ ├── assign-div.json │ ├── assign-mult.json │ ├── assign-sub.json │ ├── assign.json │ ├── break.json │ ├── choice.json │ ├── comp-eq.json │ ├── comp-gt.json │ ├── comp-gte.json │ ├── comp-lt.json │ ├── comp-lte.json │ ├── comp-ne.json │ ├── div.json │ ├── elif.json │ ├── else.json │ ├── f-str-item.json │ ├── f-str.json │ ├── false.json │ ├── find.json │ ├── floor-div.json │ ├── for.json │ ├── if.json │ ├── import.json │ ├── in.json │ ├── input.json │ ├── join.json │ ├── len.json │ ├── list-append.json │ ├── list-element-assign.json │ ├── list-index.json │ ├── list-item.json │ ├── list-literal.json │ ├── mod.json │ ├── mult.json │ ├── not-in.json │ ├── not.json │ ├── num.json │ ├── or.json │ ├── print.json │ ├── randint.json │ ├── range.json │ ├── replace.json │ ├── split.json │ ├── str.json │ ├── sub.json │ ├── to-int.json │ ├── to-str.json │ ├── true.json │ └── while.json ├── editor │ ├── accordion.ts │ ├── action-executor.ts │ ├── action-filter.ts │ ├── consts.ts │ ├── cursor.ts │ ├── data-types.ts │ ├── doc-box.ts │ ├── draft.ts │ ├── editor.ts │ ├── event-router.ts │ ├── event-stack.ts │ ├── focus.ts │ ├── hole.ts │ ├── toolbox.ts │ └── validator.ts ├── index.html ├── index.ts ├── logger │ ├── analytics.ts │ ├── requests.ts │ └── user.ts ├── messages │ ├── error-msg-generator.ts │ ├── message-controller.ts │ ├── messages.ts │ └── notifications.ts ├── pyodide-js │ ├── load-pyodide.js │ └── pyodide-controller.js ├── pyodide-ts │ └── pyodide-ui.ts ├── suggestions │ ├── construct-doc.ts │ └── suggestions-controller.ts ├── syntax-tree │ ├── ast.ts │ ├── body.ts │ ├── callback.ts │ ├── consts.ts │ ├── module.ts │ ├── scope.ts │ ├── tree-array.ts │ ├── type-checker.ts │ └── variable-controller.ts └── utilities │ ├── text-enhance.ts │ └── util.ts ├── tsconfig.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | lib 4 | logs 5 | *.log 6 | npm-debug.log* 7 | .env 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "useTabs": false, 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["elif", "randint", "consts", "pyodide", "builtins"], 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.organizeImports": true 6 | }, 7 | "editor.tabSize": 4 8 | 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nova-editor", 3 | "version": "1.0.0", 4 | "description": "A new text-based environment that helps beginners transition into conventional text-based programming environments.", 5 | "keywords": [ 6 | "programming", 7 | "cs-education", 8 | "novice", 9 | "beginner", 10 | "editor" 11 | ], 12 | "author": "Majeed Kazemitaabar, Viktar Chyhir", 13 | "license": "GPL-3.0", 14 | "scripts": { 15 | "start": "webpack serve --config webpack.config.js --open", 16 | "build": "node node_modules/webpack/bin/webpack.js --progress" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.24.0", 20 | "fuse.js": "^6.4.6", 21 | "monaco-editor": "^0.25.2" 22 | }, 23 | "devDependencies": { 24 | "@webpack-cli/serve": "^1.5.1", 25 | "css-loader": "^5.2.6", 26 | "file-loader": "^6.2.0", 27 | "html-webpack-plugin": "^5.3.2", 28 | "monaco-editor-webpack-plugin": "^4.0.0", 29 | "style-loader": "^3.0.0", 30 | "terser-webpack-plugin": "^5.1.4", 31 | "ts-loader": "^9.2.3", 32 | "typescript": "^4.4.2", 33 | "webpack": "^5.43.0", 34 | "webpack-cli": "^4.7.2", 35 | "webpack-dev-server": "^3.11.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # CodeStruct 2 | A new text-based environment that helps beginners transition into conventional text-based programming environments. 3 | 4 | Features: 5 | - avoids syntax errors 6 | - enables structured text-based editing 7 | - provides learning moments on invalid attempts 8 | - provides hints and visual descriptions 9 | 10 | Authoring code with CodeStruct: 11 | - Cursor-aware Toolbox 12 | - Suggestion Menus and Autocomplete 13 | - Draft Mode Editing 14 | -------------------------------------------------------------------------------- /src/css/accordion.css: -------------------------------------------------------------------------------- 1 | .accordion-group-container { 2 | } 3 | 4 | .accordion-row { 5 | border-radius: 4px; 6 | margin: 8px; 7 | background-color: #fff; 8 | overflow: hidden; 9 | box-shadow: 0px 1px 3px 1px #167faa; 10 | } 11 | 12 | .accordion-row .header-container { 13 | display: flex; 14 | justify-content: space-between; 15 | font-size: 14px; 16 | cursor: pointer; 17 | 18 | transition: 0.15s ease-in-out; 19 | -webkit-transition: 0.15s ease-in-out; 20 | -moz-transition: 0.15s ease-in-out; 21 | -o-transition: 0.15s ease-in-out; 22 | -ms-transition: 0.15s ease-in-out; 23 | } 24 | 25 | .accordion-row .header-container:hover { 26 | background-color: #ccc; 27 | } 28 | 29 | .accordion-row .header-container:hover .expand-collapse-button svg { 30 | background-color: #aebbcf; 31 | color: #00f; 32 | } 33 | 34 | .accordion-row .row-icon { 35 | display: flex; 36 | margin-left: 5px; 37 | align-items: center; 38 | } 39 | .accordion-row .row-type { 40 | margin-left: 5px; 41 | margin-top: 4px; 42 | } 43 | .accordion-row .row-title { 44 | margin-left: 5px; 45 | color: #000; 46 | font-weight: bold; 47 | } 48 | .accordion-row .row-chevron-right-icon { 49 | display: flex; 50 | margin-left: 5px; 51 | } 52 | .accordion-row .expand-collapse-button { 53 | display: flex; 54 | } 55 | 56 | .accordion-row .expand-collapse-button svg { 57 | cursor: pointer; 58 | margin: 2px; 59 | cursor: pointer; 60 | border-radius: 99px; 61 | color: #000; 62 | 63 | transition: 0.15s ease-in-out; 64 | -webkit-transition: 0.15s ease-in-out; 65 | -moz-transition: 0.15s ease-in-out; 66 | -o-transition: 0.15s ease-in-out; 67 | -ms-transition: 0.15s ease-in-out; 68 | } 69 | 70 | .accordion-row .content-container { 71 | overflow: hidden; 72 | } 73 | 74 | .accordion-row .header-container .type-container { 75 | background-color: #670093; 76 | padding-right: 8px; 77 | display: flex; 78 | flex-direction: row; 79 | align-content: center; 80 | height: 100%; 81 | } 82 | .accordion-row .header-container .text-container { 83 | display: flex; 84 | flex-direction: row; 85 | align-items: center; 86 | } 87 | 88 | .accordion-row .header-container .bg-learn { 89 | background-color: #9c27b0; 90 | } 91 | .accordion-row .header-container .bg-try { 92 | background-color: #45a249; 93 | } 94 | .accordion-row .header-container .bg-hint { 95 | background-color: #cc451b; 96 | } 97 | 98 | .accordion-row .chevron { 99 | color: #000; 100 | padding: 10px; 101 | 102 | transition: background-color 0.15s ease-in-out; 103 | -webkit-transition: background-color 0.15s ease-in-out; 104 | -moz-transition: background-color 0.15s ease-in-out; 105 | -o-transition: background-color 0.15s ease-in-out; 106 | -ms-transition: background-color 0.15s ease-in-out; 107 | } 108 | 109 | .accordion-row .chevron:hover { 110 | color: #00f; 111 | } 112 | -------------------------------------------------------------------------------- /src/css/cascaded-menu.css: -------------------------------------------------------------------------------- 1 | .cascadedMenuMainDiv::-webkit-scrollbar { 2 | background: #ccc; 3 | margin: 5px; 4 | width: 8px; 5 | } 6 | 7 | .cascadedMenuMainDiv::-webkit-scrollbar-track { 8 | background: #ebebeb; 9 | } 10 | 11 | .cascadedMenuMainDiv::-webkit-scrollbar-thumb { 12 | border: none; 13 | background-color: #d1d1d1; 14 | border-radius: 0px; 15 | } 16 | 17 | .cascadedMenuMainDiv { 18 | opacity: 0; 19 | 20 | min-width: 375px; 21 | z-index: 10; 22 | position: absolute; 23 | background-color: white; 24 | box-shadow: 0px 8px 16px 0px rgb(0 0 0 / 20%); 25 | 26 | border-radius: 6px; 27 | 28 | overflow-y: auto; 29 | 30 | transition: opacity 0.1s ease-in-out; 31 | -webkit-transition: opacity 0.1s ease-in-out; 32 | -moz-transition: opacity 0.1s ease-in-out; 33 | -o-transition: opacity 0.1s ease-in-out; 34 | -ms-transition: opacity 0.1s ease-in-out; 35 | } 36 | 37 | .cascadedMenuItem { 38 | cursor: pointer; 39 | } 40 | 41 | .cascadedMenuMainDiv .cascadedMenuItem :not(:hover) { 42 | background-color: white; 43 | } 44 | 45 | .cascadedMenuContent { 46 | padding-left: 9px; 47 | } 48 | 49 | .cascadedMenuContent .inline-var-id { 50 | color: #aa5bc8; 51 | border-radius: 4px; 52 | padding: 1px 4px; 53 | border: solid 1px #bbb; 54 | } 55 | 56 | .cascadedMenuOptionTooltip { 57 | display: inline; 58 | align-self: center; 59 | font-size: 14px; 60 | color: #16b3c1; 61 | margin-right: 10px; 62 | } 63 | 64 | .valid-option-tooltip { 65 | font-weight: bold; 66 | } 67 | 68 | .hoverable:hover .cascadedMenuMainDiv { 69 | display: block; 70 | } 71 | 72 | .cascaded-menu-header { 73 | border-bottom: solid 1px #ccc; 74 | margin-bottom: 5px; 75 | height: 35px; 76 | width: 100%; 77 | display: flex; 78 | flex-direction: row; 79 | align-items: center; 80 | background-color: #006a93; 81 | color: #fff; 82 | } 83 | 84 | .cascaded-menu-header h3 { 85 | font-weight: bold; 86 | font-size: 18px; 87 | margin: 8px 10px 8px 10px; 88 | } 89 | 90 | .cascaded-menu-header .identifier { 91 | font-weight: bold; 92 | background-color: #9850b3; 93 | padding: 0px 8px 0px 8px; 94 | border-radius: 3px; 95 | color: #fff; 96 | } 97 | 98 | .cascadedMenuContent .button { 99 | font-size: 15px; 100 | padding: 6px 10px; 101 | 102 | white-space: nowrap; 103 | box-shadow: 0px 1px 5px 2px #ddd; 104 | 105 | transition: 0.15s ease-in-out; 106 | -webkit-transition: 0.15s ease-in-out; 107 | -moz-transition: 0.15s ease-in-out; 108 | -o-transition: 0.15s ease-in-out; 109 | -ms-transition: 0.15s ease-in-out; 110 | 111 | -webkit-user-select: none; /* Safari */ 112 | -moz-user-select: none; /* Firefox */ 113 | -ms-user-select: none; /* IE10+/Edge */ 114 | user-select: none; /* Standard */ 115 | } 116 | 117 | .cascadedMenuContent .button:hover { 118 | box-shadow: 0px 1px 5px 2px #ccc; 119 | } 120 | 121 | .cascadedMenuContent .var-button-container { 122 | display: inline-flex; 123 | } 124 | 125 | .cascadedMenuContent .button-id { 126 | color: #aa5bc8; 127 | } 128 | 129 | .var-more-actions-button { 130 | border: solid 1px #007acc; 131 | padding: 3px 6px; 132 | border-radius: 5px; 133 | color: #007acc; 134 | font-size: 13px; 135 | 136 | transition: background-color 0.15s ease-in-out; 137 | -webkit-transition: background-color 0.15s ease-in-out; 138 | -moz-transition: background-color 0.15s ease-in-out; 139 | -o-transition: background-color 0.15s ease-in-out; 140 | -ms-transition: background-color 0.15s ease-in-out; 141 | 142 | cursor: pointer; 143 | 144 | margin-right: 5px; 145 | } 146 | 147 | .var-more-actions-button:hover { 148 | background-color: #007acc; 149 | color: #fff; 150 | } 151 | -------------------------------------------------------------------------------- /src/css/console.css: -------------------------------------------------------------------------------- 1 | #console { 2 | font-family: Consolas, "Courier New", monospace; 3 | 4 | flex-shrink: 0; 5 | flex-grow: 0; 6 | flex-basis: 30%; 7 | height: 30%; 8 | width: 100%; 9 | 10 | display: flex; 11 | flex-flow: column; 12 | 13 | background-color: #ccc; 14 | box-shadow: 10px 0px 20px 0px #ccc; 15 | } 16 | 17 | .run-code-btn { 18 | justify-self: start; 19 | background-color: rgb(24 200 120); 20 | color: #fff !important; 21 | } 22 | 23 | .run-code-button-container { 24 | margin: 10px 0px 0px 20px; 25 | } 26 | 27 | .run-code-button-container .disabled { 28 | background-color: #888; 29 | cursor: not-allowed; 30 | } 31 | 32 | #outputDiv { 33 | color: #000; 34 | font-size: 14px; 35 | font-weight: bold; 36 | 37 | overflow-y: auto; 38 | border-top: solid #bcbcbc 1px; 39 | 40 | margin-top: 10px; 41 | padding-left: 20px; 42 | padding: 8px 0 8px 20px; 43 | } 44 | 45 | .consoleTxt { 46 | font-weight: 10pt; 47 | } 48 | 49 | .consoleErrTxt { 50 | color: red; 51 | } 52 | 53 | .consoleWarnTxt { 54 | color: orange; 55 | } 56 | 57 | .clear-output-btn { 58 | box-shadow: none; 59 | border: solid #4f4f4f 0.5px; 60 | } 61 | 62 | .console-button { 63 | color: #000; 64 | cursor: pointer; 65 | border-radius: 4px; 66 | 67 | display: inline-block; 68 | align-items: center; 69 | justify-content: center; 70 | 71 | opacity: 1; 72 | 73 | height: -webkit-fit-content; 74 | height: -moz-fit-content; 75 | height: fit-content; 76 | 77 | margin-right: 10px; 78 | box-shadow: rgb(0 0 0 / 20%) 0px 3px 1px -2px, rgb(0 0 0 / 14%) 0px 2px 2px 0px, rgb(0 0 0 / 12%) 0px 1px 5px 0px; 79 | 80 | font-size: 14px; 81 | font-weight: bold; 82 | 83 | padding: 8px 10px; 84 | 85 | width: -webkit-fit-content; 86 | width: -moz-fit-content; 87 | width: fit-content; 88 | 89 | transition: 0.2s ease-in-out; 90 | -webkit-transition: 0.2s ease-in-out; 91 | -moz-transition: 0.2s ease-in-out; 92 | -o-transition: 0.2s ease-in-out; 93 | -ms-transition: 0.2s ease-in-out; 94 | } 95 | 96 | .run-code-btn:hover { 97 | background-color: rgb(21 173 110); 98 | box-shadow: rgb(0 0 0 / 20%) 0px 2px 4px -1px, rgb(0 0 0 / 14%) 0px 4px 5px 0px, rgb(0 0 0 / 12%) 0px 1px 10px 0px; 99 | } 100 | 101 | .clear-output-btn:hover { 102 | background-color: #4f4f4f; 103 | color: #fff; 104 | } 105 | 106 | #outputDiv::-webkit-scrollbar { 107 | background: #ccc; 108 | margin: 5px; 109 | width: 8px; 110 | } 111 | 112 | #outputDiv::-webkit-scrollbar-track { 113 | background: #c1c1c1; 114 | } 115 | 116 | #outputDiv::-webkit-scrollbar-thumb { 117 | border: none; 118 | background-color: #8c8c8c; 119 | border-radius: 0px; 120 | } 121 | -------------------------------------------------------------------------------- /src/css/construct-doc.css: -------------------------------------------------------------------------------- 1 | .docParent { 2 | position: absolute; 3 | left: 200px; 4 | top: 200px; 5 | 6 | max-width: 50%; 7 | max-height: 30%; 8 | min-width: 40%; 9 | min-height: 20%; 10 | width: fit-content; 11 | height: fit-content; 12 | overflow-y: scroll; 13 | 14 | border-style: solid; 15 | border-width: 2px; 16 | border-color: grey; 17 | 18 | background-color: rgb(197, 197, 197); 19 | 20 | padding: 2px 2px 2px 2px; 21 | 22 | overflow-y: scroll; 23 | scrollbar-width: none; /* Firefox */ 24 | -ms-overflow-style: none; /* Internet Explorer 10+ */ 25 | } 26 | .docParent::-webkit-scrollbar { 27 | width: 0; 28 | height: 0; 29 | } 30 | 31 | .docTitle { 32 | padding: 2px; 33 | margin: 0px; 34 | border-bottom: solid 1px; 35 | border-color: black; 36 | } 37 | 38 | .docBody { 39 | padding-left: 1px; 40 | padding-right: 1px; 41 | padding-top: 1px; 42 | padding-bottom: 1px; 43 | font-size: 10pt; 44 | } 45 | 46 | .docImageParent { 47 | display: grid; 48 | grid-template-columns: 50% 50%; 49 | grid-template-rows: auto; 50 | column-gap: 10px; 51 | row-gap: 15px; 52 | margin-top: 10px; 53 | 54 | padding-right: 10px; 55 | padding-top: 2px; 56 | padding-bottom: 1px; 57 | } 58 | 59 | .docImage { 60 | max-width: 100%; 61 | max-height: 100%; 62 | min-width: 100%; 63 | min-height: 100%; 64 | } 65 | -------------------------------------------------------------------------------- /src/css/cursor.css: -------------------------------------------------------------------------------- 1 | .custom-selection-cursor { 2 | position: absolute; 3 | background: #a4e2ff94; 4 | mix-blend-mode: multiply; 5 | 6 | transition: 0.1s ease-in; 7 | pointer-events: none; 8 | z-index: 5; 9 | border-radius: 8.5px; 10 | 11 | /* this should match the border-width of hole in hole.css */ 12 | padding: 1px; 13 | } 14 | -------------------------------------------------------------------------------- /src/css/doc-box.css: -------------------------------------------------------------------------------- 1 | .doc-box-container { 2 | position: absolute; 3 | box-shadow: 0px 0px 20px 0px #5d5d5d; 4 | width: 400px; 5 | height: 330px; 6 | left: calc(50% - 150px); 7 | top: calc(50% - 125px); 8 | background-color: #fff; 9 | border-radius: 7px; 10 | overflow: hidden; 11 | resize: both; 12 | 13 | font-family: Consolas, "Courier New", monospace; 14 | } 15 | 16 | .focused-header { 17 | background-color: #6699aa !important; 18 | } 19 | 20 | .doc-box-header { 21 | height: 25px; 22 | display: flex; 23 | flex-direction: row; 24 | align-items: center; 25 | 26 | background-color: #6699aa70; 27 | 28 | transition: background-color 0.15s ease-in-out; 29 | -webkit-transition: background-color 0.15s ease-in-out; 30 | -moz-transition: background-color 0.15s ease-in-out; 31 | -o-transition: background-color 0.15s ease-in-out; 32 | -ms-transition: background-color 0.15s ease-in-out; 33 | 34 | cursor: grab; 35 | } 36 | 37 | .doc-box-header:hover { 38 | background-color: #578; 39 | } 40 | 41 | .doc-title { 42 | color: #fff; 43 | font-size: 16px; 44 | margin: 0 0 0 15px; 45 | } 46 | 47 | .close-button { 48 | margin-left: auto; 49 | margin-top: 1px; 50 | font-weight: bold; 51 | color: #fff; 52 | font-size: 27px; 53 | 54 | transition: background-color 0.15s ease-in-out; 55 | -webkit-transition: background-color 0.15s ease-in-out; 56 | -moz-transition: background-color 0.15s ease-in-out; 57 | -o-transition: background-color 0.15s ease-in-out; 58 | -ms-transition: background-color 0.15s ease-in-out; 59 | 60 | cursor: pointer; 61 | padding: 0px 10px; 62 | } 63 | 64 | .close-button:hover { 65 | color: #000; 66 | } 67 | 68 | .doc-body { 69 | overflow-y: auto; 70 | width: calc(100% - 30px); 71 | height: calc(100% - 25px); 72 | padding: 0 15px 0 15px; 73 | font-family: monospace; 74 | font-size: 15px; 75 | } 76 | 77 | .doc-editor { 78 | width: 100%; 79 | border: solid 1px #eee; 80 | } 81 | 82 | .doc-body::-webkit-scrollbar { 83 | background: #ccc; 84 | margin: 5px; 85 | width: 8px; 86 | } 87 | 88 | .doc-body::-webkit-scrollbar-track { 89 | background: #ebebeb; 90 | } 91 | 92 | .doc-body::-webkit-scrollbar-thumb { 93 | border: none; 94 | background-color: #b8ccd1; 95 | border-radius: 0px; 96 | } 97 | 98 | div.doc-body p span.italics { 99 | font-style: italic; 100 | } 101 | 102 | div.doc-body p span.code { 103 | background-color: #ddd; 104 | border-radius: 3px; 105 | padding: 1px 5px; 106 | font-weight: bold; 107 | } 108 | 109 | div.doc-body p span.bold { 110 | font-weight: bold; 111 | } 112 | 113 | div.doc-body > div.doc-editor:nth-last-child(1) { 114 | margin-bottom: 7%; 115 | } 116 | 117 | .doc-editor-container { 118 | border-top: solid 1px #ccc; 119 | } 120 | 121 | .console-output::-webkit-scrollbar { 122 | background: #ccc; 123 | margin: 5px; 124 | width: 8px; 125 | } 126 | 127 | .console-output::-webkit-scrollbar-track { 128 | background: #c1c1c1; 129 | } 130 | 131 | .console-output::-webkit-scrollbar-thumb { 132 | border: none; 133 | background-color: #8c8c8c; 134 | border-radius: 0px; 135 | } 136 | 137 | .doc-example-console { 138 | background-color: #ccc; 139 | font-size: 14px; 140 | font-weight: bold; 141 | } 142 | 143 | .doc-example-console p { 144 | padding: 0; 145 | margin: 0; 146 | } 147 | 148 | .doc-editor-header { 149 | background-color: #17a564; 150 | color: #fff; 151 | font-weight: bold; 152 | padding: 4px 8px; 153 | font-size: 14px; 154 | } 155 | 156 | .doc-example-btn { 157 | padding: 5px 10px !important; 158 | font-size: 12px !important; 159 | } 160 | 161 | .reset-editor-btn { 162 | visibility: hidden; 163 | } 164 | 165 | .block-vs-text-table-container { 166 | border-radius: 5px; 167 | box-shadow: 0 0 5px 1px #888; 168 | } 169 | 170 | .block-vs-text-table-header { 171 | height: 20px; 172 | background-color: #d9e7ee; 173 | display: flex; 174 | flex-direction: row; 175 | justify-content: space-between; 176 | align-items: center; 177 | padding: 5px 15px; 178 | font-size: 12px; 179 | font-weight: bold; 180 | } 181 | 182 | .image-container { 183 | flex-direction: row; 184 | justify-content: space-between; 185 | display: flex; 186 | align-items: start; 187 | padding: 15px; 188 | } 189 | 190 | .image-container img { 191 | width: 48%; 192 | } 193 | 194 | .console-output { 195 | height: 100px; 196 | max-height: 0px; 197 | overflow-y: scroll; 198 | 199 | color: #000; 200 | 201 | transition: max-height 0.2s ease-in-out; 202 | -webkit-transition: max-height 0.2s ease-in-out; 203 | -moz-transition: max-height 0.2s ease-in-out; 204 | -o-transition: max-height 0.2s ease-in-out; 205 | -ms-transition: max-height 0.2s ease-in-out; 206 | } 207 | 208 | .console-output-open { 209 | padding: 5px; 210 | max-height: 100px; 211 | } 212 | 213 | .doc-example-console-button-container { 214 | border-bottom: solid 1px#bbb; 215 | padding: 5px; 216 | } 217 | -------------------------------------------------------------------------------- /src/css/draft-mode.css: -------------------------------------------------------------------------------- 1 | .draftModeLine { 2 | background-color: rgba(255, 0, 0, 0.3); 3 | position: absolute; 4 | pointer-events: none; 5 | border-radius: 5px; 6 | } 7 | 8 | .draftModeLine.fixed { 9 | background-color: rgba(30, 255, 0, 0.3); 10 | } 11 | -------------------------------------------------------------------------------- /src/css/editor.css: -------------------------------------------------------------------------------- 1 | #editorArea { 2 | width: 100%; 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | #editor { 9 | width: 100%; 10 | flex-shrink: 0; 11 | flex-grow: 0; 12 | flex-basis: 70%; 13 | height: 70%; 14 | } 15 | 16 | #editor .monaco-editor.no-user-select.showUnused.showDeprecated.vs { 17 | height: 100%; 18 | } 19 | 20 | #editor .monaco-editor, 21 | #editor .monaco-editor-background, 22 | #editor .monaco-editor .inputarea.ime-input { 23 | background-color: rgba(0, 0, 0, 0) !important; 24 | } 25 | 26 | #editor .monaco-editor-background { 27 | /* background-color: var(--main-bg-color) !important; */ 28 | background-color: rgba(0, 0, 0, 0) !important; 29 | } 30 | 31 | #editor .monaco-editor .margin { 32 | /* background-color: var(--main-bg-color) !important; */ 33 | background-color: rgba(0, 0, 0, 0) !important; 34 | } 35 | 36 | #editor .monaco-editor .view-overlays .current-line { 37 | display: none; 38 | } 39 | 40 | #editor .execution-decoration { 41 | left: 45px !important; 42 | 43 | width: 8px !important; 44 | height: 8px !important; 45 | 46 | border-radius: 50%; 47 | border: 1px solid #bfbebe; 48 | background-color: #00000008 !important; 49 | 50 | margin-top: 10%; 51 | 52 | display: none; 53 | } 54 | 55 | #editor .monaco-editor .line-numbers { 56 | color: black !important; 57 | opacity: 0.2; 58 | font-size: 12px !important; 59 | margin-top: 2px !important; 60 | transition: 1s; 61 | transition-property: opacity; 62 | margin-left: -10px; 63 | } 64 | 65 | #editor .monaco-editor .line-numbers:hover { 66 | opacity: 1 !important; 67 | } 68 | 69 | #editor .line-selected { 70 | font-weight: 800 !important; 71 | opacity: 1 !important; 72 | } 73 | 74 | #editor .cigr { 75 | box-shadow: 0.5px 0 0 0 rgba(0, 0, 0, 0) inset !important; 76 | } 77 | 78 | #editor .cigra { 79 | box-shadow: 0.5px 0 0 0 rgba(0, 0, 0, 0) inset !important; 80 | } 81 | 82 | #editor .monaco-editor .margin-view-overlays .codicon-chevron-down { 83 | font-size: 80%; 84 | } 85 | 86 | #editor .monaco-editor .margin-view-overlays .codicon-folding-collapsed, 87 | #editor .monaco-editor .margin-view-overlays .codicon-folding-expanded { 88 | font-size: 80% !important; 89 | } 90 | 91 | #editor .cdr.bracket-match { 92 | opacity: 0.5; 93 | border: none !important; 94 | outline: none !important; 95 | border-radius: 3px; 96 | padding: 2px; 97 | background: none !important; 98 | } 99 | 100 | #editor .monaco-editor .cursors-layer .cursor { 101 | background-color: black !important; 102 | transition-property: top, left; 103 | transition: 0.1s; 104 | opacity: 0.5; 105 | height: 20px !important; 106 | margin-top: 11px !important; 107 | margin-left: 2px !important; 108 | } 109 | 110 | #editor .monaco-editor .scroll-decoration { 111 | box-shadow: none !important; 112 | } 113 | 114 | #editor .monaco-scrollable-element { 115 | overflow: visible !important; 116 | } 117 | 118 | #editor .monaco-editor .view-overlays .current-line { 119 | border: none !important; 120 | } 121 | 122 | #editor .monaco-editor .selected-text { 123 | background: none !important; 124 | } 125 | -------------------------------------------------------------------------------- /src/css/hole.css: -------------------------------------------------------------------------------- 1 | .hole { 2 | position: absolute; 3 | pointer-events: none; 4 | 5 | /* box-shadow: 0px 0px 3px 1px #00000029 inset; */ 6 | border: solid 1px #00000029; 7 | transition-property: background; 8 | transition-duration: 0.2s; 9 | 10 | border-radius: 5px; 11 | } 12 | 13 | .editableHole { 14 | /* box-shadow: 0px 0px 3px 1px #398dfab6 inset; */ 15 | border: solid 1px #398dfab6; 16 | } 17 | 18 | .validVarIdentifierHole { 19 | background-color: rgba(58, 223, 58, 0.3); 20 | } 21 | 22 | .draftVarIdentifierHole { 23 | background-color: rgb(255, 255, 230, 0.6); 24 | } 25 | 26 | .expression-hole { 27 | border-radius: 15px !important; 28 | } 29 | 30 | .text-editable-expr-hole { 31 | border-radius: 15px !important; 32 | border-style: dashed !important; 33 | } 34 | 35 | .empty-operator-hole { 36 | border-radius: 0 !important; 37 | } 38 | 39 | .identifier-hole { 40 | border-radius: 0 !important; 41 | border-style: dashed !important; 42 | } 43 | 44 | .errorTooltipHole { 45 | position: relative; 46 | top: 5px; 47 | } 48 | -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | @import url("doc-box.css"); 2 | @import url("editor.css"); 3 | @import url("toolbox.css"); 4 | @import url("cursor.css"); 5 | @import url("hole.css"); 6 | @import url("messages.css"); 7 | @import url("suggestion-menu.css"); 8 | @import url("construct-doc.css"); 9 | @import url("draft-mode.css"); 10 | @import url("cascaded-menu.css"); 11 | @import url("console.css"); 12 | @import url("notification.css"); 13 | @import url("tooltip.css"); 14 | @import url("accordion.css"); 15 | 16 | @import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400;1,600&display=swap"); 17 | 18 | html { 19 | height: 100%; 20 | } 21 | 22 | body { 23 | margin: 0px; 24 | width: 100%; 25 | height: 100%; 26 | overflow: hidden; 27 | font-family: Source Sans Pro; 28 | } 29 | 30 | #editor-container { 31 | display: flex; 32 | height: 100%; 33 | } 34 | 35 | .button { 36 | color: #0d0c22; 37 | cursor: pointer; 38 | border-radius: 4px; 39 | display: flex; 40 | align-items: center; 41 | justify-content: center; 42 | opacity: 1; 43 | height: -webkit-fit-content; 44 | height: -moz-fit-content; 45 | height: fit-content; 46 | margin-right: 10px; 47 | font-size: 14px; 48 | padding: 10px 14px; 49 | width: -webkit-fit-content; 50 | width: -moz-fit-content; 51 | width: fit-content; 52 | border: solid 2px #fff; 53 | } 54 | 55 | .button.button-invalid { 56 | opacity: 0.4; 57 | cursor: not-allowed; 58 | border: solid 2px #c5c5c5; 59 | } 60 | 61 | .button.button-valid { 62 | font-weight: bold; 63 | } 64 | 65 | .button.button-draft-mode { 66 | background-color: rgb(255 254 195); 67 | } 68 | -------------------------------------------------------------------------------- /src/css/messages.css: -------------------------------------------------------------------------------- 1 | .codeVisual { 2 | display: block; 3 | position: absolute; 4 | pointer-events: none; 5 | z-index: -999; 6 | } 7 | 8 | .codeVisual.highlight { 9 | border-radius: 5px; 10 | 11 | background-color: rgba(255, 255, 255, 0); 12 | 13 | transition: background-color 0.3s ease-in-out; 14 | -webkit-transition: background-color 0.3s ease-in-out; 15 | -moz-transition: background-color 0.3s ease-in-out; 16 | -o-transition: background-color 0.3s ease-in-out; 17 | -ms-transition: background-color 0.3s ease-in-out; 18 | } 19 | 20 | .scope-header-highlight { 21 | display: block; 22 | position: absolute; 23 | pointer-events: none; 24 | z-index: -999; 25 | 26 | border-top-left-radius: 6px; 27 | border-bottom-left-radius: 6px; 28 | border-top-right-radius: 6px; 29 | } 30 | 31 | .scope-body-highlight { 32 | display: block; 33 | position: absolute; 34 | pointer-events: none; 35 | z-index: -999; 36 | 37 | border-bottom-left-radius: 6px; 38 | border-bottom-right-radius: 6px; 39 | } 40 | 41 | .codeVisual.textBox { 42 | font-family: Consolas, "Courier New", monospace; 43 | color: #000; 44 | font-size: 14px; 45 | 46 | box-shadow: 0 0 7px 1px #888; 47 | background-color: #c5d2d9; 48 | overflow-y: scroll; 49 | overflow-wrap: break-word; 50 | scrollbar-width: none; 51 | 52 | max-width: 40vw; 53 | width: 40vw; 54 | height: fit-content; 55 | max-height: 40vh; 56 | 57 | display: block; 58 | height: fit-content; 59 | 60 | border-radius: 5px; 61 | 62 | pointer-events: all; 63 | z-index: 999; 64 | } 65 | 66 | .codeVisual.textBox::-webkit-scrollbar { 67 | display: none; 68 | } 69 | 70 | .codeVisual .text-container-style { 71 | margin-bottom: 5px; 72 | line-height: 18px; 73 | } 74 | 75 | .codeVisual .msg-content-container { 76 | padding: 8px; 77 | } 78 | 79 | .codeVisual.popUp { 80 | background-color: rgb(253, 198, 115); 81 | color: black; 82 | position: absolute; 83 | width: 125px; 84 | height: fit-content; 85 | max-height: 30%; 86 | } 87 | 88 | .identifier { 89 | color: blueviolet; 90 | font-weight: bold; 91 | } 92 | 93 | .type { 94 | font-weight: bold; 95 | background-color: #99a7ad; 96 | padding: 1px 5px 1px 5px; 97 | border-radius: 3px; 98 | } 99 | 100 | .keyword { 101 | color: blue; 102 | font-weight: 500; 103 | } 104 | 105 | .other { 106 | font-weight: 600; 107 | } 108 | 109 | .emph { 110 | font-weight: 700; 111 | } 112 | 113 | .codeVisual.textBox .button { 114 | display: inline-flex; 115 | margin: 7px 7px 0 0; 116 | font-size: 13px; 117 | box-shadow: 0 0 3px 1px #aaa; 118 | 119 | background-color: #ffffff; 120 | height: 17px; 121 | padding: 3px 8px 3px 8px; 122 | align-content: center; 123 | } 124 | 125 | .hover-msg-header { 126 | width: 100%; 127 | height: 20px; 128 | background-color: rgb(130 155 169); 129 | color: white; 130 | display: flex; 131 | flex-direction: row; 132 | align-items: center; 133 | padding-left: 8px; 134 | } 135 | -------------------------------------------------------------------------------- /src/css/notification.css: -------------------------------------------------------------------------------- 1 | .notification-container { 2 | padding: 10px; 3 | border-radius: 6px; 4 | position: fixed; 5 | bottom: calc(30% + 25px); 6 | right: 25px; 7 | width: 450px; 8 | z-index: 9999; 9 | background-color: rgb(102 65 173 / 50%); 10 | font-family: Consolas, "Courier New", monospace; 11 | 12 | opacity: 0; 13 | transform: scale(0.75); 14 | } 15 | 16 | .notification-container .message { 17 | font-size: 17px; 18 | } 19 | 20 | .notification-container .message .code { 21 | background-color: #64a; 22 | border-radius: 4px; 23 | padding: 3px 5px; 24 | color: #fff; 25 | font-weight: bold; 26 | } 27 | 28 | .animate { 29 | transition: all 0.2s ease-in-out; 30 | opacity: 1; 31 | transform: scale(1); 32 | } 33 | 34 | .key-animation-container { 35 | margin-top: 10px; 36 | } 37 | 38 | .key-animation-container .key-span { 39 | display: inline-block; 40 | margin-right: 8px; 41 | padding: 5px 5px; 42 | border-radius: 4px; 43 | background-color: #0ff; 44 | font-size: 20px; 45 | font-weight: bold; 46 | } 47 | 48 | expr-hole { 49 | border-radius: 2px; 50 | margin: 0px 3px; 51 | min-width: 21px; 52 | height: 10px; 53 | display: inline-block; 54 | } 55 | 56 | id-hole { 57 | height: 10px; 58 | width: 8px; 59 | display: inline-block; 60 | } 61 | -------------------------------------------------------------------------------- /src/css/suggestion-menu.css: -------------------------------------------------------------------------------- 1 | .suggestionMenuParent { 2 | display: flex; 3 | flex-direction: column; 4 | position: absolute; 5 | width: fit-content; 6 | max-width: 50%; 7 | min-width: 2%; 8 | 9 | overflow-y: scroll; 10 | scrollbar-width: none; /* Firefox */ 11 | -ms-overflow-style: none; /* Internet Explorer 10+ */ 12 | } 13 | .suggestionMenuParent::-webkit-scrollbar { 14 | width: 0; 15 | height: 0; 16 | } 17 | 18 | .suggestionMenuParent hole1 { 19 | border-radius: 5px; 20 | box-shadow: 0px 0px 1px 1px #0000005e inset; 21 | display: inline-block; 22 | 23 | position: relative; 24 | top: 2px; 25 | margin: 0px 2px; 26 | height: 16px; 27 | min-width: 16px; 28 | } 29 | 30 | .suggestionMenuParent hole2 { 31 | border-radius: 5px; 32 | box-shadow: 0px 0px 1px 1px #0000005e inset; 33 | display: inline-block; 34 | 35 | position: relative; 36 | top: 2px; 37 | margin: 0px 2px; 38 | box-shadow: none; 39 | border: dashed 1px #777; 40 | min-width: 9px; 41 | height: 13px; 42 | border-radius: 0; 43 | } 44 | 45 | .suggestionOptionParent { 46 | padding-left: 2px; 47 | padding-top: 2px; 48 | padding-bottom: 2px; 49 | padding-right: 2px; 50 | background-color: rgba(215, 215, 215, 0.7); 51 | cursor: pointer; 52 | 53 | height: fit-content; 54 | 55 | font-family: Consolas, "Courier New", monospace; 56 | font-weight: normal; 57 | font-feature-settings: "liga" 0, "calt" 0; 58 | } 59 | 60 | :hover.suggestionOptionParent { 61 | background-color: rgba(176, 202, 243, 0.9); 62 | } 63 | 64 | .suggestionOptionText { 65 | color: black; 66 | width: fit-content; 67 | font-size: 18px; 68 | line-height: 22px; 69 | letter-spacing: -0.5px; 70 | float: left; 71 | } 72 | 73 | .suggestionOptionExtraInfo { 74 | float: right; 75 | margin-left: 30px; 76 | margin-right: 5px; 77 | color: #555; 78 | position: relative; 79 | top: 2px; 80 | } 81 | 82 | .highlighted-text { 83 | background-color: #cc00ef; 84 | color: #fff; 85 | font-weight: bold; 86 | 87 | padding: 0 5px; 88 | border-radius: 3px; 89 | } 90 | 91 | .matchingText { 92 | color: #cc00ef; 93 | font-weight: bold; 94 | } 95 | 96 | .selectedSuggestionOptionParent { 97 | background-color: rgba(93, 149, 240, 0.9) !important; 98 | } 99 | 100 | .draftModeOptionElementClass { 101 | background-color: rgba(230, 233, 202, 0.7); 102 | } 103 | 104 | .optionArrowImage { 105 | grid-column-start: 2; 106 | grid-column-end: 2; 107 | grid-row-start: 1; 108 | grid-row-end: 1; 109 | 110 | width: 8px; 111 | height: 10px; 112 | align-self: center; 113 | justify-self: end; 114 | } 115 | 116 | .categoryHeading { 117 | font-weight: bold; 118 | } 119 | 120 | .categoryOption { 121 | padding-left: 10px; 122 | } 123 | -------------------------------------------------------------------------------- /src/css/toolbox.css: -------------------------------------------------------------------------------- 1 | #toolbox-container { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | box-shadow: 20px -50px 50px -15px #ccc; 6 | 7 | min-width: 375px; 8 | 9 | font-family: Consolas, "Courier New", monospace; 10 | } 11 | 12 | #editor-toolbox { 13 | display: flex; 14 | flex-direction: column; 15 | 16 | overflow-x: hidden; 17 | overflow-y: scroll; 18 | 19 | border-left: solid 1px #ddd; 20 | 21 | height: calc(100% - 75px); 22 | } 23 | 24 | #editor-toolbox .button { 25 | font-size: 15px; 26 | padding: 6px 10px; 27 | 28 | white-space: nowrap; 29 | box-shadow: 0px 1px 5px 2px #ddd; 30 | 31 | transition: 0.15s ease-in-out; 32 | -webkit-transition: 0.15s ease-in-out; 33 | -moz-transition: 0.15s ease-in-out; 34 | -o-transition: 0.15s ease-in-out; 35 | -ms-transition: 0.15s ease-in-out; 36 | 37 | -webkit-user-select: none; /* Safari */ 38 | -moz-user-select: none; /* Firefox */ 39 | -ms-user-select: none; /* IE10+/Edge */ 40 | user-select: none; /* Standard */ 41 | } 42 | 43 | #editor-toolbox .button:hover { 44 | box-shadow: 0px 1px 5px 2px #ccc; 45 | } 46 | 47 | hole1 { 48 | min-width: 25px; 49 | height: 15px; 50 | border-radius: 10px; 51 | box-shadow: 0px 0px 1px 1px #0000005e inset; 52 | margin: 0px 3px; 53 | display: inline-block; 54 | } 55 | 56 | hole2 { 57 | margin: 0px 3px; 58 | display: inline-block; 59 | box-shadow: none; 60 | border: dashed 1px #c7c7c7; 61 | min-width: 12px; 62 | height: 16px; 63 | } 64 | 65 | #user-variables { 66 | height: calc(100% - 25px); 67 | overflow-y: auto; 68 | } 69 | 70 | .var-button { 71 | font-size: 16px; 72 | padding: 3px 14px; 73 | white-space: nowrap; 74 | box-shadow: 0px 1px 5px 2px #ddd; 75 | transition: 0.15s ease-in-out; 76 | -webkit-transition: 0.15s ease-in-out; 77 | font-weight: bold; 78 | background-color: #b200c4; 79 | border-radius: 25px; 80 | cursor: pointer; 81 | color: #fff; 82 | } 83 | 84 | .var-button:hover { 85 | box-shadow: 0px 1px 5px 2px #b200c499; 86 | } 87 | 88 | .var-container-wrapper { 89 | display: flex; 90 | margin-left: 5px; 91 | } 92 | 93 | .var-button-container { 94 | display: flex; 95 | align-items: center; 96 | justify-content: space-between; 97 | 98 | padding: 5px 0; 99 | border-radius: 6px; 100 | 101 | box-shadow: 0 0 0 0 #5de1a5; 102 | 103 | transition: 0.5s ease-in-out; 104 | -webkit-transition: 0.5s ease-in-out; 105 | -moz-transition: 0.5s ease-in-out; 106 | -o-transition: 0.5s ease-in-out; 107 | -ms-transition: 0.5s ease-in-out; 108 | } 109 | 110 | .glowing { 111 | box-shadow: 0 0 10px 6px #5de1a5; 112 | background-color: #c6f5e0; 113 | } 114 | 115 | #editor-toolbox .group { 116 | justify-items: start; 117 | margin: 0 0 20px 10px; 118 | } 119 | 120 | #editor-toolbox .group > p { 121 | grid-column: 1 / span 2; 122 | grid-row: 1 / span 1; 123 | font-weight: bold; 124 | font-size: 20px; 125 | 126 | margin: 5px 0 5px 0; 127 | } 128 | 129 | #editor-toolbox::-webkit-scrollbar { 130 | background: #ccc; 131 | margin: 5px; 132 | width: 8px; 133 | } 134 | 135 | #editor-toolbox::-webkit-scrollbar-track { 136 | background: #ebebeb; 137 | } 138 | 139 | #editor-toolbox::-webkit-scrollbar-thumb { 140 | border: none; 141 | background-color: #b8ccd1; 142 | border-radius: 0px; 143 | } 144 | 145 | #user-variables::-webkit-scrollbar { 146 | background: #ccc; 147 | margin: 5px; 148 | width: 8px; 149 | } 150 | 151 | #user-variables::-webkit-scrollbar-track { 152 | background: #ebebeb; 153 | } 154 | 155 | #user-variables::-webkit-scrollbar-thumb { 156 | border: none; 157 | background-color: #b8ccd1; 158 | border-radius: 0px; 159 | } 160 | 161 | #toolbox-menu { 162 | float: left; 163 | } 164 | 165 | #toolbox-menu .menu-button { 166 | border-radius: 3px; 167 | padding: 5px 10px 5px 10px; 168 | cursor: pointer; 169 | color: #26265a; 170 | font-size: 16px; 171 | 172 | transition: 0.15s ease-in-out; 173 | -webkit-transition: 0.15s ease-in-out; 174 | -moz-transition: 0.15s ease-in-out; 175 | -o-transition: 0.15s ease-in-out; 176 | -ms-transition: 0.15s ease-in-out; 177 | } 178 | 179 | #toolbox-menu .menu-button:hover { 180 | background-color: #e0efff; 181 | font-weight: bold; 182 | } 183 | 184 | #static-toolbox { 185 | height: 70%; 186 | } 187 | 188 | #dynamic-toolbox { 189 | height: 30%; 190 | } 191 | 192 | .box-header { 193 | height: 25px; 194 | background-color: #69a; 195 | color: #fff; 196 | 197 | display: flex; 198 | flex-direction: column; 199 | justify-content: center; 200 | } 201 | 202 | .box-header h2 { 203 | display: block; 204 | margin-block-end: 0px; 205 | margin-block-start: 0px; 206 | font-size: 18px; 207 | font-weight: bold; 208 | margin-left: 10px; 209 | } 210 | 211 | #vars-button-grid { 212 | margin: 5px; 213 | } 214 | 215 | .learn-button { 216 | position: relative; 217 | border-radius: 5px; 218 | border: solid 1px #fff; 219 | padding: 0px 10px; 220 | margin-right: 10px; 221 | font-size: 14px; 222 | color: #006a93; 223 | display: inline-flex; 224 | flex-direction: column; 225 | justify-content: center; 226 | font-weight: bold; 227 | align-items: center; 228 | box-shadow: 0 0 6px 1px rgb(0 71 99); 229 | font-family: Consolas, "Courier New", monospace; 230 | height: 20px; 231 | cursor: pointer; 232 | background-color: #fff; 233 | 234 | transition: background-color 0.15s ease-in-out; 235 | -webkit-transition: background-color 0.15s ease-in-out; 236 | -moz-transition: background-color 0.15s ease-in-out; 237 | -o-transition: background-color 0.15s ease-in-out; 238 | -ms-transition: background-color 0.15s ease-in-out; 239 | } 240 | 241 | .tooltip-container { 242 | font-family: Consolas, "Courier New", monospace; 243 | position: fixed; 244 | display: none; 245 | color: #fff; 246 | border-radius: 10px; 247 | width: 500px; 248 | overflow: hidden; 249 | background: rgb(0 162 225); 250 | box-shadow: 0px 8px 16px 0px rgb(0 0 0 / 20%); 251 | opacity: 0; 252 | 253 | transition: opacity 0.1s ease-in-out; 254 | -webkit-transition: opacity 0.1s ease-in-out; 255 | -moz-transition: opacity 0.1s ease-in-out; 256 | -o-transition: opacity 0.1s ease-in-out; 257 | -ms-transition: opacity 0.1s ease-in-out; 258 | } 259 | 260 | .tooltip-container .tooltip-top { 261 | background-color: rgb(0 106 147); 262 | font-size: 14px; 263 | } 264 | 265 | .tooltip-container .tooltip-header { 266 | display: flex; 267 | flex-direction: row; 268 | align-items: center; 269 | justify-content: space-between; 270 | } 271 | 272 | .tooltip-container .tooltip-header h4 { 273 | font-weight: bold; 274 | font-size: 16px; 275 | margin: 0; 276 | padding: 10px; 277 | } 278 | 279 | .tooltip-container .tooltip-text { 280 | font-size: 14px; 281 | padding: 0 10px 10px; 282 | margin: 0; 283 | } 284 | 285 | .tooltip-container .error-text { 286 | padding: 5px 10px; 287 | color: #000; 288 | background-color: rgb(255 200 200); 289 | box-shadow: 0px 0px 10px 0px rgb(0 65 72); 290 | } 291 | 292 | .tooltip-container .warning-text { 293 | padding: 5px 10px; 294 | color: #000; 295 | background-color: rgb(255 255 205); 296 | box-shadow: 0px 0px 10px 0px rgb(0 65 72); 297 | } 298 | 299 | .tooltip-container .return-type-text { 300 | padding: 5px 10px; 301 | background-color: rgb(0 133 147); 302 | box-shadow: 0px 0px 10px 0px rgb(0 65 72); 303 | } 304 | 305 | .tooltip-container .return-type-text .return-type { 306 | padding: 0 5px; 307 | border-radius: 3px; 308 | background-color: #8d97a3; 309 | color: #000; 310 | font-weight: bold; 311 | } 312 | 313 | .tooltip-container .learn-button:hover { 314 | background-color: rgb(0 106 147); 315 | color: #fff; 316 | } 317 | 318 | .tooltip-container .use-cases-container { 319 | max-height: 300px; 320 | overflow-y: scroll; 321 | background-color: #fff; 322 | padding-bottom: 10px; 323 | } 324 | 325 | .tooltip-container .spacing { 326 | height: 5px; 327 | } 328 | 329 | .tooltip-container .use-cases-container::-webkit-scrollbar { 330 | background: #ccc; 331 | margin: 5px; 332 | width: 8px; 333 | } 334 | 335 | .tooltip-container .use-cases-container::-webkit-scrollbar-track { 336 | background: #ebebeb; 337 | } 338 | 339 | .tooltip-container .use-cases-container::-webkit-scrollbar-thumb { 340 | border: none; 341 | background-color: #b8ccd1; 342 | border-radius: 0px; 343 | } 344 | 345 | .tooltip-container .quick-tip { 346 | border-top: solid 1px #ccc; 347 | padding: 5px; 348 | color: #000; 349 | font-size: 14px; 350 | } 351 | 352 | .tooltip-container .quick-tip .quick-tip-text { 353 | font-size: 13px; 354 | color: #000; 355 | } 356 | 357 | .tooltip-container .quick-tip .quick-tip-title { 358 | font-size: 13px; 359 | color: #fff; 360 | background-color: #00aeae; 361 | padding: 1px 4px; 362 | border-radius: 3px; 363 | margin-right: 5px; 364 | font-weight: bold; 365 | } 366 | 367 | .tooltip-container .single-use-case-container { 368 | color: #000; 369 | font-weight: bold; 370 | border-top: solid 1px #ccc; 371 | } 372 | 373 | .tooltip-container .single-use-case-container .use-case-title { 374 | padding: 5px 5px 5px 10px; 375 | font-weight: bold; 376 | font-size: 13px; 377 | cursor: pointer; 378 | display: flex; 379 | justify-content: space-between; 380 | 381 | transition: background-color 0.15s ease-in-out; 382 | -webkit-transition: background-color 0.15s ease-in-out; 383 | -moz-transition: background-color 0.15s ease-in-out; 384 | -o-transition: background-color 0.15s ease-in-out; 385 | -ms-transition: background-color 0.15s ease-in-out; 386 | } 387 | 388 | .tooltip-container .single-use-case-container .use-case-title:hover { 389 | background-color: #cfe3eb !important; 390 | } 391 | 392 | .tooltip-container .single-use-case-container .use-case-title .use-case-title-header { 393 | /* margin-top: 4px; */ 394 | } 395 | 396 | .tooltip-container .single-use-case-container .use-case-learn-button { 397 | color: #000000; 398 | cursor: pointer; 399 | border-radius: 4px; 400 | font-size: 14px; 401 | padding: 3px 10px; 402 | border: solid 1px #006a938c; 403 | background-color: #006a931f; 404 | 405 | transition: background-color 0.2s ease-in-out; 406 | -webkit-transition: background-color 0.2s ease-in-out; 407 | -moz-transition: background-color 0.2s ease-in-out; 408 | -o-transition: background-color 0.2s ease-in-out; 409 | -ms-transition: background-color 0.2s ease-in-out; 410 | 411 | transition: opacity 0.4 ease-in-out; 412 | -webkit-transition: opacity 0.4 ease-in-out; 413 | -moz-transition: opacity 0.4 ease-in-out; 414 | -o-transition: opacity 0.4 ease-in-out; 415 | -ms-transition: opacity 0.4 ease-in-out; 416 | } 417 | 418 | .tooltip-container .single-use-case-container .use-case-learn-button:hover { 419 | background-color: #006a9338; 420 | } 421 | 422 | .tooltip-container .slider-container { 423 | font-family: Consolas, "Courier New", monospace; 424 | 425 | transition: max-height 0.15s ease-in-out; 426 | -webkit-transition: max-height 0.15s ease-in-out; 427 | -moz-transition: max-height 0.15s ease-in-out; 428 | -o-transition: max-height 0.15s ease-in-out; 429 | -ms-transition: max-height 0.15s ease-in-out; 430 | } 431 | 432 | .tooltip-container .slider-container .range-slider { 433 | display: inline-block; 434 | flex-grow: 1; 435 | } 436 | 437 | .tooltip-container .slider-container .slider-btn { 438 | display: inline-block; 439 | cursor: pointer; 440 | -webkit-user-select: none; 441 | -moz-user-select: none; 442 | -ms-user-select: none; 443 | user-select: none; 444 | background-color: #7017ff; 445 | margin: 0 10px 0; 446 | padding: 5px 10px; 447 | border-radius: 5px; 448 | font-weight: bold; 449 | box-shadow: rgb(0 0 0 / 20%) 0px 3px 1px -2px, rgb(0 0 0 / 14%) 0px 2px 2px 0px, rgb(0 0 0 / 12%) 0px 1px 5px 0px; 450 | color: #fff; 451 | } 452 | 453 | .tooltip-container .slider-container .slider-image { 454 | width: 470px; 455 | display: block; 456 | } 457 | 458 | .tooltip-container .slider-container .labels-container { 459 | display: flex; 460 | font-size: 14px; 461 | justify-content: center; 462 | } 463 | 464 | .tooltip-container .slider-container .slider-label { 465 | border: solid 2px #00719e; 466 | margin: 10px 0 10px 10px; 467 | padding: 3px 7px; 468 | border-radius: 5px; 469 | background-color: #0a82b1; 470 | color: #ffffff; 471 | } 472 | 473 | .tooltip-container .slider-container .explanation-container { 474 | margin: 5px; 475 | padding: 3px 7px; 476 | border-radius: 5px; 477 | background-color: #7017ff; 478 | color: #ffffff; 479 | position: relative; 480 | } 481 | 482 | .tooltip-container .slider-container .explanation-container::after { 483 | content: ""; 484 | position: absolute; 485 | bottom: 98%; 486 | left: 49%; 487 | margin-left: -5px; 488 | border-width: 7px; 489 | border-style: solid; 490 | border-color: transparent transparent #7017ff transparent; 491 | } 492 | 493 | .tooltip-container .slider-container .buttons-container { 494 | display: flex; 495 | margin-bottom: 5px; 496 | } 497 | 498 | .statement-button { 499 | border-bottom: solid #fff0 3.5px; 500 | 501 | transition: border 0.15s ease-in-out; 502 | -webkit-transition: border 0.15s ease-in-out; 503 | -moz-transition: border 0.15s ease-in-out; 504 | -o-transition: border 0.15s ease-in-out; 505 | -ms-transition: border 0.15s ease-in-out; 506 | } 507 | 508 | .statement-button:hover { 509 | border-bottom: solid #05c8ac 3.5px !important; 510 | } 511 | 512 | .expression-button { 513 | border: solid #fff0 3px; 514 | padding-left: 10px !important; 515 | padding-right: 10px !important; 516 | border-radius: 25px !important; 517 | 518 | transition: border 0.15s ease-in-out; 519 | -webkit-transition: border 0.15s ease-in-out; 520 | -moz-transition: border 0.15s ease-in-out; 521 | -o-transition: border 0.15s ease-in-out; 522 | -ms-transition: border 0.15s ease-in-out; 523 | } 524 | 525 | .expression-button:hover { 526 | border: solid #05c8ac 3px !important; 527 | } 528 | 529 | .modifier-button { 530 | border-left: solid #fff0 3.5px; 531 | 532 | transition: border 0.15s ease-in-out; 533 | -webkit-transition: border 0.15s ease-in-out; 534 | -moz-transition: border 0.15s ease-in-out; 535 | -o-transition: border 0.15s ease-in-out; 536 | -ms-transition: border 0.15s ease-in-out; 537 | } 538 | 539 | .modifier-button:hover { 540 | border-left: solid #05c8ac 3.5px !important; 541 | } 542 | 543 | .var-type-text { 544 | align-self: center; 545 | font-size: 14px; 546 | margin-left: 10px; 547 | } 548 | 549 | .search-box { 550 | height: 30px; 551 | width: calc(100% - 20px); 552 | margin: 10px; 553 | box-shadow: 0px 0px 6px 1px #ccc inset; 554 | border: 1px solid #ccc; 555 | border-radius: 6px; 556 | padding: 7px; 557 | font-family: Consolas, "Courier New", monospace; 558 | 559 | transition: box-shadow 0.15s ease-in-out; 560 | -webkit-transition: box-shadow 0.15s ease-in-out; 561 | -moz-transition: box-shadow 0.15s ease-in-out; 562 | -o-transition: box-shadow 0.15s ease-in-out; 563 | -ms-transition: box-shadow 0.15s ease-in-out; 564 | } 565 | 566 | .search-box:hover { 567 | box-shadow: 0px 0px 6px 1px #aaa inset; 568 | font-weight: bold; 569 | } 570 | 571 | .search-box:focus-visible { 572 | border: 1px solid #bbb; 573 | outline: none; 574 | font-weight: bold; 575 | } 576 | 577 | .def-vars-type-title-span { 578 | font-style: italic; 579 | } 580 | 581 | .def-vars-type-span { 582 | font-style: normal; 583 | font-weight: bold; 584 | color: #006bff; 585 | } 586 | 587 | .defined-var-container { 588 | border-bottom: solid #ccc 1px; 589 | flex-direction: column; 590 | display: flex; 591 | } 592 | 593 | .immediate-tooltip { 594 | display: inline-block; 595 | box-shadow: 0px 1px 5px 2px #ddd; 596 | padding: 3px 8px; 597 | border-radius: 2px; 598 | margin-left: 6px; 599 | } 600 | 601 | .immediate-tooltips-container { 602 | font-size: 13px; 603 | } 604 | 605 | .cascaded-menu-extra-container { 606 | background-color: #008593; 607 | padding: 10px; 608 | } 609 | 610 | .cascaded-menu-extra-item { 611 | border-radius: 4px; 612 | border: solid 1px #ddd; 613 | padding: 5px 10px; 614 | color: #fff; 615 | } 616 | 617 | .cascaded-menu-extra-item:not(:last-child) { 618 | margin-bottom: 5px; 619 | } 620 | 621 | .cascaded-menu-extra-item .code { 622 | color: #0f0fff; 623 | border-radius: 4px; 624 | padding: 0px 6px; 625 | background-color: #fff; 626 | font-weight: bold; 627 | } 628 | 629 | .cascaded-menu-extra-item .inline-var { 630 | color: #aa5bc8; 631 | } 632 | -------------------------------------------------------------------------------- /src/css/tooltip.css: -------------------------------------------------------------------------------- 1 | .tooltip-top-container { 2 | width: 100%; 3 | background-color: black; 4 | color: #fff; 5 | text-align: center; 6 | border-radius: 6px; 7 | padding: 5px 0; 8 | position: absolute; 9 | z-index: 1; 10 | top: 150%; 11 | left: 50%; 12 | margin-left: -60px; 13 | } 14 | 15 | .tooltip-top-container::after { 16 | content: ""; 17 | position: absolute; 18 | bottom: 100%; 19 | left: 50%; 20 | margin-left: -5px; 21 | border-width: 5px; 22 | border-style: solid; 23 | border-color: transparent transparent black transparent; 24 | } 25 | 26 | .tooltip-left-container { 27 | width: 100%; 28 | background-color: black; 29 | color: #fff; 30 | text-align: center; 31 | border-radius: 6px; 32 | padding: 5px 0; 33 | position: absolute; 34 | z-index: 1; 35 | top: -5px; 36 | left: 110%; 37 | } 38 | 39 | .tooltip-left-container::after { 40 | content: ""; 41 | position: absolute; 42 | top: 50%; 43 | right: 100%; 44 | margin-top: -5px; 45 | border-width: 5px; 46 | border-style: solid; 47 | border-color: transparent black transparent transparent; 48 | } 49 | -------------------------------------------------------------------------------- /src/docs/add-var.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Create/Reassign Variable", 3 | "tooltip": { 4 | "title": "Create Variable Assignment", 5 | "body": "Allows to store a value and give it a name to reference it by later." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-declare-print-var", 11 | "example": "name = \"John\"\nprint(name)" 12 | }, 13 | { 14 | "type": "quick", 15 | "text": "A variable is created the moment you first assign a value to it." 16 | }, 17 | { 18 | "type": "bullet-point", 19 | "title": "variable naming", 20 | "bullets": [ 21 | "Variable names can only contain letters, numbers, and underscores", 22 | "Variable names are case-sensitive (\"x\" and \"X\" are different)", 23 | "A variable name must start with a letter or the underscore character", 24 | "A variable name cannot start with a number" 25 | ] 26 | } 27 | ], 28 | "search-queries": ["create new variable", "variable", "var"] 29 | } 30 | -------------------------------------------------------------------------------- /src/docs/add.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "add", 3 | "tooltip": { 4 | "title": "Insert Addition", 5 | "body": "Adds the values to the left and right of the operator." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-sum-numbers", 11 | "example": "a = 2\nb = 5\nprint((a + b))" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-sum-strings", 16 | "example": "a = \"hello\"\nb = \"world\"\nprint((a + \" \" + b))" 17 | } 18 | ], 19 | "search-queries": ["add", "sum"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/and.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "and", 3 | "tooltip": { 4 | "title": "And Operator", 5 | "body": "Used to combine conditional expressions. Results in True if both the left and right expressions are True; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-and-four-cases", 11 | "example": "if (true and true):\n\tprint(true)\n\nif (true and false):\n\tprint(\"not true\")\n\nif (false and true):\n\tprint(\"still not true\")\n\nif (false and false):\n\tprint(\"also false\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-gt-and-gt", 16 | "example": "a = 5\nb = 7\nc=2\n\nif ((a > c) and (b > c)):\n\tprint(\"Both expressions are true.\")" 17 | } 18 | ], 19 | "search-queries": ["and", "both", "and operator"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/assign-add.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Add to Variable", 3 | "tooltip": { 4 | "title": "Add Value to Variable", 5 | "body": "Adds the value on the right-hand side of the += sign to the specified variable and stores the result in the variable." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-var-sum-by-val", 11 | "example": "a = 2\na += 2\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["add to variable"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/assign-div.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Divide a Variable", 3 | "tooltip": { 4 | "title": "Divide Variable by Value", 5 | "body": "Divides the variable by the value given on the right-hand side of the /= sign and stores the result back in the variable." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-var-divide-by-value", 11 | "example": "a = 10\na /= 2\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["divide variable"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/assign-mult.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Multiply a Variable", 3 | "tooltip": { 4 | "title": "Multiply Variable by Value", 5 | "body": "Multiplies the variable by the value on the right-hand side of the *= sign and stores the result back in the variable." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-var-mul-by-val", 11 | "example": "a = 5\na *= 2\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["multiply variable"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/assign-sub.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Subtract from Variable", 3 | "tooltip": { 4 | "title": "Subtract Value from Variable", 5 | "body": "Subtracts the value on the right-hand side of the -= sign from the specified variable and stores the result back in the variable." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-var-sub-by-val", 11 | "example": "a = 5\na -= 2\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["subtract from variable", "deduct from variable"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/assign.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Assign to Variable", 3 | "tooltip": { 4 | "title": "Update Value of Variable", 5 | "body": "Allows assigning a new value to an existing variable." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-assign-val-var-twice", 11 | "example": "a = 123\nprint(a)\na=321\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["set", "assign", "assign variable", "set variable", "update value variable"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/break.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Break out of loops", 3 | "tooltip": { 4 | "title": "Insert Break inside Loop", 5 | "body": "Stops the innermost running loop (for, while) and breaks out of it." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-break-out-range", 11 | "example": "for i in range(0, 50):\n\tif i == 25:\n\t\tbreak\n\tprint(i)" 12 | }, 13 | { 14 | "type": "use-case", 15 | "title": "break out of a while loop on some condition", 16 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/3-break/", 17 | "max": 30, 18 | "extension": "PNG", 19 | "prefix": "Slide", 20 | "id": "break-while-on-condition", 21 | "explanations": [ 22 | { "slide": 5, "text": "variable i is defined and set to 0" }, 23 | { "slide": 6, "text": "should repeat the while statements if i < 10" }, 24 | { "slide": 7, "text": "i is 0 and less than 10 -> should repeat" }, 25 | { "slide": 9, "text": "i is 0, so i + 1 is 1" }, 26 | { "slide": 11, "text": "i becomes 1" }, 27 | { "slide": 12, "text": "should enter the if statements if i equals 3" }, 28 | { "slide": 13, "text": "i is 1 so shouldn't enter the if" }, 29 | { "slide": 16, "text": "i is 1, so i + 1 is 2" }, 30 | { "slide": 14, "text": "should continue repeating because i is 1" }, 31 | { "slide": 18, "text": "i becomes 2" }, 32 | { "slide": 20, "text": "i is not equal to 3" }, 33 | { "slide": 21, "text": "should continue repeating because i is 2" }, 34 | { "slide": 23, "text": "i is 2, so i + 1 is 3" }, 35 | { "slide": 25, "text": "i becomes 3" }, 36 | { "slide": 27, "text": "is equal to 3 -> should enter if" }, 37 | { "slide": 28, "text": "break out of the current loop" }, 38 | { "slide": 29, "text": "won't check the while condition as we used break" }, 39 | { "slide": 30, "text": "will print 'done'" } 40 | ] 41 | } 42 | ], 43 | "search-queries": ["exit", "loop", "break"] 44 | } 45 | -------------------------------------------------------------------------------- /src/docs/choice.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "choice(choices: list[any])", 3 | "tooltip": { 4 | "title": "Insert Choice from List", 5 | "body": "Randomly selects and returns an item from the given list. Needs to be imported from the random module." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-choice", 11 | "example": "from random import choice\n\nif choice([1, 2, 3, 4, 5, 6]) == 6:\n\tprint(\"Rolled Six!\")" 12 | } 13 | ], 14 | "search-queries": ["choice", "random choice", "choose randomly from list", "select randomly from array"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/comp-eq.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Is Equal?", 3 | "tooltip": { 4 | "title": "Is Equal?", 5 | "body": "Compares two values. Returns True if they are equal; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-is-eq-1", 11 | "example": "a = 2\nb = 3\nif a == c:\n\tprint(\"a is equal to c\")\n\nif b == a:\n\tprint(\"This will not print because the condition is false.\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-is-eq-2", 16 | "example": "a = 5\nb = 5\nwhile a == b:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["compare equal", "check equal", "are equal"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/comp-gt.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Is Greater Than?", 3 | "tooltip": { 4 | "title": "Is Greater Than?", 5 | "body": "Compares two values. Returns True if the left value is greater than the right value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-gt-1", 11 | "example": "a = 2\nb = 3\nif b > a:\n\tprint(\"b is greater than a\")\n\nif a > b:\n\tprint(\"a is greater to b\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-gt-2", 16 | "example": "a = 1\nb = 5\nwhile b > a:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["compare greater than", "compare", "check greater than"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/comp-gte.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "is greater than or equal?", 3 | "tooltip": { 4 | "title": "Is Greater Than or Equal?", 5 | "body": "Compares two values. Returns True if the left value is greater than or equal to the right value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-gte-1", 11 | "example": "a = 2\nb = 3\n c = 2\n if b >= a:\n\tprint(\"b is greater than a\")\n\nif c >= a:\n\tprint(\"c is equal to a\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-gt-1", 16 | "example": "a = 1\nb = 5\nwhile b >= a:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["compare greater than or equal", "compare", "check greater than or equal"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/comp-lt.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Is Less Than?", 3 | "tooltip": { 4 | "title": "Is Less Than?", 5 | "body": "Compares two values. Returns True if the left value is less than the right value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-lt-1", 11 | "example": "a = 2\nb = 3\nif a < b:\n\tprint(\"a is less than b\")\n\nif c < a:\n\tprint(\"This will not print because the condition is false.\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-lt-2", 16 | "example": "a = 1\nb = 5\nwhile a < b:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["compare", "compare less than", "check less than"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/comp-lte.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "is less than or equal?", 3 | "tooltip": { 4 | "title": "Is Less Than or Equal?", 5 | "body": "Compares two values. Returns True if the left value is less than or equal to the right value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-leq-1", 11 | "example": "a = 2\nb = 3\n c = 2\n if a <= b:\n\tprint(\"a is less than b\")\n\nif c <= a:\n\tprint(\"c is equal to a\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-leq-2", 16 | "example": "a = 1\nb = 5\nwhile a <= b:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["less than or equal", "compare", "compare less than or equal", "check"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/comp-ne.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Is Not Equal?", 3 | "tooltip": { 4 | "title": "Is Not Equal?", 5 | "body": "Compares two values. Returns True if they are not equal; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-not-equal-1", 11 | "example": "a = 2\nb = 3\nif a != b:\n\tprint(\"a is not equal to b\")\n\nif c != a:\n\tprint(\"This will not print because the condition is false.\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-not-equal-2", 16 | "example": "a = 1\nb = 5\nwhile a != b:\n\ta += 1\nprint(a)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["not equal", "compare not equal", "not equal to", "are not equal"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/div.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "divide", 3 | "tooltip": { 4 | "title": "Insert Division", 5 | "body": "Divides the values to the left of the operator by the right value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-div-two-vars", 11 | "example": "a = 2\nb = 10\nprint((b / a))" 12 | } 13 | ], 14 | "search-queries": ["divide", "division", "divide numbers"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/elif.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "elif statement", 3 | "tooltip": { 4 | "title": "Insert Elif Statement", 5 | "body": "Short for else-if; adds another case to an existing if statement. The `elif` will only run when previous `if` or `elif` statement is false" 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-elif-1", 11 | "example": "a = 3\nif a > 10:\n\tprint(\"a is larger than 10\")\nelif a < 10:\n\tprint(\"a is smaller than 10\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-elif-2", 16 | "example": "a = 5\nif a > 3:\n\tprint(3)\nelif a > 4:\n\tprint(4)\nelif a == 5:\n\tprint(5)\nelif a > 6:\n\tprint(6)" 17 | }, 18 | { 19 | "type": "use-case", 20 | "title": "using elif after an if statement", 21 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/7-if-single-elif/", 22 | "max": 11, 23 | "extension": "PNG", 24 | "prefix": "Slide", 25 | "id": "elif-after-if" 26 | }, 27 | { 28 | "type": "use-case", 29 | "title": "using elif after another elif statement", 30 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/7-if-double-elif/", 31 | "max": 13, 32 | "extension": "PNG", 33 | "prefix": "Slide", 34 | "id": "elif-after-elif" 35 | } 36 | ], 37 | "search-queries": ["else if", "else", "else condition", "choose", "path", "elif"] 38 | } 39 | -------------------------------------------------------------------------------- /src/docs/else.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "else statement", 3 | "tooltip": { 4 | "title": "Insert Else Statement", 5 | "body": "Can be used after an if or an elif statement. will execute its block of code when the if and the elif statements were not true" 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-else-1", 11 | "example": "a = 2\nif a > 3:\n\tprint(3)\nelif a > 4:\n\tprint(4)\nelif a == 5:\n\tprint(5)\nelif a > 6:\n\tprint(6)\nelse:\n\tprint(\"None of the above are true.\")" 12 | } 13 | ], 14 | "search-queries": ["else", "otherwise", "if"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/f-str-item.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "formatted text item", 3 | "tooltip": { 4 | "title": "Formatted Text Item", 5 | "body": "Converts the item to its textual value. Can only be used inside a formatted text (f'')." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-f-str-item", 11 | "example": "age = 16\nname = \"Alex\"\nprint(f'My name is {name} and I am {age} years old')" 12 | } 13 | ], 14 | "search-queries": ["formatted string item", "formatted text item"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/f-str.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "formatted text", 3 | "tooltip": { 4 | "title": "Insert Formattable Text", 5 | "body": "Inserts an editable, formattable text; used along with the {} operator to include non-static values and variables inside the text." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-f-str-1", 11 | "example": "age = 16\nname = \"Alex\"\nprint(f'My name is {name} and I am {age} years old')" 12 | } 13 | ], 14 | "search-queries": ["formatted string", "formatted text"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/false.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "false", 3 | "tooltip": { 4 | "title": "Insert False", 5 | "body": "Inserts a False boolean value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-false", 11 | "example": "if False :\n\tprint(\"will not print\")\n\nif True :\n\tprint(\"will print\")" 12 | } 13 | ], 14 | "search-queries": ["false", "boolean"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/find.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "find(txt: text)", 3 | "tooltip": { 4 | "title": "Call Find Method", 5 | "body": "Finds the first occurrence of the input's textual value in the text." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-find-1", 11 | "example": "name = \"Zimmer\"\nindexOfE = name.find(\"e\")\nprint(indexOfE)" 12 | } 13 | ], 14 | "search-queries": ["search text", "find text", "find string"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/floor-div.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "floor division", 3 | "tooltip": { 4 | "title": "Insert Floor Division", 5 | "body": "Performs integer division (remainder is discarded and always result is always rounded down) between the values to the left and right of the operator." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-floor-div", 11 | "example": "a = 2\nb = 7\nprint((b / a))" 12 | } 13 | ], 14 | "search-queries": ["divide numbers", "divide integer", "divide floor"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/for.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "for loop", 3 | "tooltip": { 4 | "title": "loop through a sequence of elements", 5 | "body": "executes a set of statements for each element in a sequence (like a `range`, a `list`, or a `string`). " 6 | }, 7 | "tips": [ 8 | { 9 | "type": "quick", 10 | "title": "for loop inputs", 11 | "text": "for loops have two inputs: a variable name, and a sequence of items. the created variable is used to hold the current item, and the sequence is the list of items to go through." 12 | }, 13 | { 14 | "type": "quick", 15 | "title": "for loop as a counter", 16 | "text": "for loops can be used with the `range` function to work like a counter" 17 | }, 18 | { 19 | "type": "executable", 20 | "title": "using a for loop to go through a list", 21 | "id": "ex-for-list-items", 22 | "example": "lst = [\"cat\", \"dog\", \"mouse\", \"parrot\"]\nfor animal in lst:\n\tprint(animal)" 23 | }, 24 | { 25 | "type": "use-case", 26 | "title": "go through a list of items", 27 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/2-for-loop-list/", 28 | "max": 25, 29 | "extension": "PNG", 30 | "prefix": "Slide", 31 | "id": "loop-list-items" 32 | }, 33 | { 34 | "type": "use-case", 35 | "title": "go through a sequence of numbers", 36 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/3-for-loop-range/", 37 | "max": 21, 38 | "extension": "PNG", 39 | "prefix": "Slide", 40 | "id": "loop-sequence-nums" 41 | }, 42 | { 43 | "type": "use-case", 44 | "title": "go through a list of items using indices", 45 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/4-for-loop-range-list/", 46 | "max": 30, 47 | "extension": "PNG", 48 | "prefix": "Slide", 49 | "id": "loop-list-items-using-indices" 50 | }, 51 | { 52 | "type": "use-case", 53 | "title": "go through every character of a string", 54 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/5-for-loop-string/", 55 | "max": 21, 56 | "extension": "PNG", 57 | "prefix": "Slide", 58 | "id": "loop-chars-of-str" 59 | } 60 | ], 61 | "search-queries": ["for", "loop", "repeat", "go through", "iterate", "list"] 62 | } 63 | -------------------------------------------------------------------------------- /src/docs/if.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "if statement", 3 | "tooltip": { 4 | "title": "Insert If Statement", 5 | "body": "Will only execute the indented block of code below it when the condition is true" 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-if-1", 11 | "example": "a = 3\nif a < 10:\n\tprint(\"Success!\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-if-2", 16 | "example": "a = 3\nif a > 10:\n\tprint(\"Success!\")" 17 | }, 18 | { 19 | "type": "use-case", 20 | "title": "check the value of a variable", 21 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/6-if/", 22 | "max": 13, 23 | "extension": "PNG", 24 | "prefix": "Slide", 25 | "id": "check-val-variable" 26 | } 27 | ], 28 | "search-queries": ["if", "condition", "conditional", "choose", "path"] 29 | } 30 | -------------------------------------------------------------------------------- /src/docs/import.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Import Module", 3 | "tooltip": { 4 | "title": "Imports A Function", 5 | "body": "Loads a particular function from a module." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-import-randint-and-choice", 11 | "example": "from random import randint\nfrom random import choice" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-import-randint-use", 16 | "example": "from random import randint\nprint(randint(1, 6))" 17 | } 18 | ], 19 | "search-queries": ["import module", "import random"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/in.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Inside?", 3 | "tooltip": { 4 | "title": "Inside?", 5 | "body": "Returns True if the left item is inside the right item; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-in-list", 11 | "example": "from random import randint\na = randint(1, 10)\nb = [1, 2, 3, 4, 5]\nif a in b:\n\tprint(\"a is inside b\")\n\nif a not in b:\n\tprint(\"a is not inside b\")" 12 | } 13 | ], 14 | "search-queries": ["in list", "inside list", "is inside list", "check inside list", "check list includes"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/input.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "input(prompt: text)", 3 | "tooltip": { 4 | "title": "Ask User for Textual Input", 5 | "body": "Displays a message that prompts the user to enter some text. The entered text will be returned." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-input-1", 11 | "example": "name = input(\"Hi! What’s your name?\")\nprint(name)" 12 | } 13 | ], 14 | "search-queries": ["input", "prompt", "text", "user", "ask input", "ask user"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/join.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "join(elements: list[text])", 3 | "tooltip": { 4 | "title": "Call Join Method", 5 | "body": "Uses this text as a separator to join a list of values together into a new text." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-join-1", 11 | "example": "names = [\"Anna\", \"John\", \"Peter\"]\njoinedNames = \"-\".join(names)\nprint(joinedNames)" 12 | } 13 | ], 14 | "search-queries": ["join string", "join text"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/len.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "len(iterable: list/text)", 3 | "tooltip": { 4 | "title": "Get Length of List or Text", 5 | "body": "Returns the number of items in an object, or the number of characters in a text." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-len-1", 11 | "example": "greeting = \"Hello World!\"\na = len(greeting)\nprint(a)" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-len-2", 16 | "example": "items = [1, 2, 3]\na = len(items)\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["length of list", "length of string", "length of text"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/list-append.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "append(element: object)", 3 | "tooltip": { 4 | "title": "Append Element to List", 5 | "body": "Appends (adds to the end of) a new element to the list." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-append-1", 11 | "example": "a = [1, 2, 3]\na.append(4)\nprint(a)" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-append-2", 16 | "example": "print([1, 2, 3].append(4))" 17 | } 18 | ], 19 | "search-queries": [ 20 | "add to list", 21 | "append to list", 22 | "insert into list", 23 | "add to end list", 24 | "append to end list", 25 | "insert end list" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /src/docs/list-element-assign.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "List Element Assignment", 3 | "tooltip": { 4 | "title": "", 5 | "body": "" 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-list-el-assign-1", 11 | "example": "a = [1, 2, 3]\na[2] = 4\nprint(a)" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-list-el-assign-2", 16 | "example": "a = [1, 2, 3]\na[1] = \"cat\"\nprint(a)" 17 | } 18 | ], 19 | "search-queries": [ 20 | "assign element at index", 21 | "change value list index", 22 | "update item at index", 23 | "set value list index" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/docs/list-index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "List Element Access", 3 | "tooltip": { 4 | "title": "Access List Element", 5 | "body": "Adds a list accessor to a list (or a text) to access an element of the list at the provided index." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-list-index-1", 11 | "example": "a = [1, 2, 3]\nprint(a[0])" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-list-index-2", 16 | "example": "a = [1, 2, 3]\nif a[1] == 2:\n\tprint(\"The second element of the list is equal to 2.\")" 17 | } 18 | ], 19 | "search-queries": ["access list item", "access list element", "access list element at index"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/list-item.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "List Item", 3 | "tooltip": { 4 | "title": "Insert List Element", 5 | "body": "Inserts a new empty element inside a list." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-list-item", 11 | "example": "a = []\nb = [1, 2]\nc = [1, 2, 3]" 12 | } 13 | ], 14 | "search-queries": ["comma", "list item"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/list-literal.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "List", 3 | "tooltip": { 4 | "title": "Insert Editable List", 5 | "body": "Inserts an empty list. Press comma before or after each item to add a new empty item." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-list-1", 11 | "example": "a = []\nprint(a) # a is an empty list\na = [1, 2, 3]\nprint(a) # a is a list with three items" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-list-1", 16 | "example": "a = [\"cat\", \"dog\", \"parrot\"]\nb = [1, \"cat\", true]\nc = [1, [1, \"dog\"], \"cat\", [true]]" 17 | } 18 | ], 19 | "search-queries": ["empty list", "create empty list", "create list", "array", "create empty array"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "modulo", 3 | "tooltip": { 4 | "title": "Insert Modulo", 5 | "body": "Calculates the remainder of the division between the values to the left and right of the operator." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-modulo", 11 | "example": "print((29 % 5)) # prints 4\nprint((28 % 5)) # prints 3\nprint((27 % 5)) # prints 2\nprint((26 % 5)) # prints 1\nprint((25 % 5)) # prints 0" 12 | } 13 | ], 14 | "search-queries": ["modulo", "remainder"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/mult.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "multiply", 3 | "tooltip": { 4 | "title": "Insert Multiplication", 5 | "body": "Multiplies the values to the left and right of the operator." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-mul-vars", 11 | "example": "a = 2\nb = 5\nprint((b * a))" 12 | } 13 | ], 14 | "search-queries": ["multiply numbers", "multiply"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/not-in.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Not Inside?", 3 | "tooltip": { 4 | "title": "Not Inside?", 5 | "body": "Returns True if the left item is not inside the right item; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-not-inside-rand-list", 11 | "example": "from random import randint\na = randint(1, 10)\nb = [1, 2, 3, 4, 5]\nif a in b:\n\tprint(\"a is inside b\")\n\nif a not in b:\n\tprint(\"a is not inside b\")" 12 | } 13 | ], 14 | "search-queries": ["not in", "not inside list", "not within list", "does not include"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/not.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "not", 3 | "tooltip": { 4 | "title": "Not Operator", 5 | "body": "Flips the truth value of an expression. True becomes False, and False becomes True." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-not", 11 | "example": "a = 2\nb = 2\nif not (a == b):\n\tprint(\"Will not print.\")\n\nif not (b != a):\n\tprint(\"b is equal to a\")" 12 | } 13 | ], 14 | "search-queries": ["negate", "not", "logical", "boolean", "opposite"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/num.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "123", 3 | "tooltip": { 4 | "title": "Insert a Number", 5 | "body": "Inserts a number and edit its value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-num", 11 | "example": "age = 21\nprint(age)\nprint(21)" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/docs/or.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "or", 3 | "tooltip": { 4 | "title": "Or Operator", 5 | "body": "Used to combine conditional expressions. Results in True as long as at least one of the left and right expressions is True; returns False otherwise." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-or-1", 11 | "example": "if (true or true):\n\tprint(true)\n\nif (true or false):\n\tprint(\"true\")\n\nif (false or true):\n\tprint(\"still true\")\n\nif (false or false):\n\tprint(\"false\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-or-2", 16 | "example": "a = 5\nb = 7\nc=2\n\nif ((a > c) and (b < c)):\n\tprint(\"Only the first expression is true.\")" 17 | } 18 | ], 19 | "search-queries": ["conditional", "or", "logical", "boolean", "one of"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/print.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "print(message: any)", 3 | "tooltip": { 4 | "title": "Display Text in Console", 5 | "body": "Displays the textual value of its input in the console." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-print-num-str", 11 | "example": "print(123)\nprint(\"Hello World!\")" 12 | }, 13 | { 14 | "type": "executable", 15 | "id": "ex-print-var", 16 | "example": "a = \"abc\"\nprint(a)" 17 | } 18 | ], 19 | "search-queries": ["output", "say", "print", "print output", "console", "write", "see output"] 20 | } 21 | -------------------------------------------------------------------------------- /src/docs/randint.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "randint(min: number, max: number)", 3 | "tooltip": { 4 | "title": "Generate a Random Number", 5 | "body": "Returns a randomly generated number from the given range. Needs to be imported from the random module." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-randint", 11 | "example": "a = randint(0, 10)\nprint(a)" 12 | } 13 | ], 14 | "search-queries": ["random number", "random integer", "randint", "random between"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/range.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "range(end: number)", 3 | "tooltip": { 4 | "title": "Create Iterable Sequence", 5 | "body": "Generates a sequence of numbers from 0 to the provided input. Usually used with a for loop." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-range", 11 | "example": "for i in range(10):\n\tprint(i)" 12 | } 13 | ], 14 | "search-queries": ["range", "sequence", "iterate", "for loop"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/replace.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "replace(old: text, new: text)", 3 | "tooltip": { 4 | "title": "Call Replace Method", 5 | "body": "Replaces all occurrences of the first input with the value of the second input in the text." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-replace", 11 | "example": "name = \"John\"\nname = name.replace(\"oh\", \"a\")\nprint(name)" 12 | } 13 | ], 14 | "search-queries": ["replace", "replace text", "replace string"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/split.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "split(separator: text)", 3 | "tooltip": { 4 | "title": "Call Split Method", 5 | "body": "Splits the text into a list of components based on the input (as a separator)." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-split", 11 | "example": "name = \"Split-this-text-on-hyphen\"\nsplitName = name.split(\"-\")\nprint(splitName)" 12 | } 13 | ], 14 | "search-queries": ["split", "split text", "split string"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/str.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "text", 3 | "tooltip": { 4 | "title": "Insert a Text", 5 | "body": "Inserts an editable text that is wrapped by double quotes." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-assign-str", 11 | "example": "fruit = \"apple\"\nprint(fruit)\nprint(\"apple\")" 12 | } 13 | ], 14 | "search-queries": ["create text", "create string", "empty string", "empty text"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/sub.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "subtract", 3 | "tooltip": { 4 | "title": "Insert Subtraction", 5 | "body": "Subtracts the value to the right of the operator from the value to the left." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-sub-vars", 11 | "example": "a = 20\nb = 10\nprint((a - b))" 12 | } 13 | ], 14 | "search-queries": ["subtract numbers", "subtraction", "deduct"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/to-int.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "int(object: string)", 3 | "tooltip": { 4 | "title": "Converts Text to Number", 5 | "body": "Converts a number text to an actual number. Extremely useful when trying to input numbers from the user." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-int-cast", 11 | "example": "a = \"2\"\nb = \"5\"\ntext_add = a + b\nnumber_add = int(a) + int(b)" 12 | } 13 | ], 14 | "search-queries": ["integer cast", "number convert", "convert to number", "convert to integer"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/to-str.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "str(object: any)", 3 | "body": [ 4 | { 5 | "paragraph": "Converts the passed object to a text object. Useful when comparing values of different types and one of them is a text value. This type of conversion is called a cast because it is temporary." 6 | }, 7 | { "example": "a = 2\nc = \"2\"\nprint(str(a) == c)" }, 8 | { 9 | "paragraph": "In the above example you would not be able to compare a to c before performing the conversion." 10 | } 11 | ], 12 | "tooltip": { 13 | "title": "Converts Anything to Text", 14 | "body": "Converts the passed object to its textual representation." 15 | }, 16 | "tips": [ 17 | { 18 | "type": "executable", 19 | "id": "ex-str-cast", 20 | "example": "a = 2\nc = \"2\"\nprint(str(a) == c)" 21 | } 22 | ], 23 | "search-queries": ["string cast", "text convert", "convert to string", "convert to text"] 24 | } 25 | -------------------------------------------------------------------------------- /src/docs/true.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "true", 3 | "tooltip": { 4 | "title": "Insert True", 5 | "body": "Inserts a True boolean value." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "id": "ex-true", 11 | "example": "if True :\n\tprint(\"will print\")\n\nif False :\n\tprint(\"will not print\")" 12 | } 13 | ], 14 | "search-queries": ["true", "boolean"] 15 | } 16 | -------------------------------------------------------------------------------- /src/docs/while.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "while loop", 3 | "tooltip": { 4 | "title": "repeatedly execute code while true", 5 | "body": "repeatedly executes the code block inside the while loop as long as the condition remains `True`." 6 | }, 7 | "tips": [ 8 | { 9 | "type": "executable", 10 | "title": "infinite loop", 11 | "id": "ex-while-1", 12 | "example": "while True :\n\tprint(\"an infinite loop\")" 13 | }, 14 | { 15 | "type": "executable", 16 | "title": "count from 0 to 10", 17 | "id": "ex-while-2", 18 | "example": "i = 0\nwhile i < 10:\n\tprint(i)\n\ti = i + 1" 19 | }, 20 | { 21 | "type": "quick", 22 | "title": "compare to scratch's repeat until block", 23 | "text": "the `while` loop in Python compared to the `repeat until` block in Scratch: the `while` loop in Python repeats code as long as the condition is `True`, and stops when the condition becomes `False`. However, the `repeat until` block in Scratch repeats code as long as the condition is `False`, and stops when the condition becomes `True`." 24 | }, 25 | { 26 | "type": "use-case", 27 | "title": "repeatedly increment a variable", 28 | "path": "https://cdn.majeed.cc/pydoc/images/use-cases/1-while-counter/", 29 | "max": 30, 30 | "prefix": "Slide", 31 | "extension": "PNG", 32 | "id": "while-increment-var", 33 | "explanations": [ 34 | { "slide": 7, "text": "i is less than 3 so the condition holds true" }, 35 | { "slide": 28, "text": "i is no longer less than 3 so the condition is false" }, 36 | { "slide": 29, "text": "should execute the next line after the while loop" } 37 | ] 38 | } 39 | ], 40 | "search-queries": [ 41 | "while", 42 | "repeat", 43 | "repeat while", 44 | "repeat until", 45 | "loop", 46 | "condition", 47 | "conditional", 48 | "repeat condition" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /src/editor/accordion.ts: -------------------------------------------------------------------------------- 1 | import { LogEvent, Logger, LogType } from "./../logger/analytics"; 2 | export enum TooltipType { 3 | StepByStepExample = "step-by-step-example", 4 | UsageHint = "usage-hint", 5 | RunnableExample = "runnable-example", 6 | } 7 | 8 | export class AccordionRow { 9 | private accordion: Accordion; 10 | private isOpen: boolean = false; 11 | private chevronElement: HTMLElement; 12 | private contentContainer: HTMLDivElement; 13 | element: HTMLDivElement; 14 | id: string; 15 | usageTime: number = 0; 16 | type: TooltipType; 17 | 18 | constructor( 19 | accordion: Accordion, 20 | id: string, 21 | type: TooltipType, 22 | title: string, 23 | content: HTMLDivElement, 24 | onClick: () => void = () => {} 25 | ) { 26 | this.type = type; 27 | this.id = id; 28 | this.accordion = accordion; 29 | 30 | this.element = document.createElement("div"); 31 | this.element.classList.add("accordion-row"); 32 | 33 | const headerContainer = document.createElement("div"); 34 | headerContainer.classList.add("header-container"); 35 | 36 | this.contentContainer = document.createElement("div"); 37 | this.contentContainer.classList.add("content-container"); 38 | this.contentContainer.style.maxHeight = "0px"; 39 | this.contentContainer.appendChild(content); 40 | 41 | this.element.appendChild(headerContainer); 42 | this.element.appendChild(this.contentContainer); 43 | 44 | const textContainer = document.createElement("div"); 45 | textContainer.classList.add("text-container"); 46 | 47 | const typeContainer = document.createElement("div"); 48 | typeContainer.classList.add("type-container"); 49 | 50 | const icon = document.createElement("i"); 51 | icon.classList.add("row-icon"); 52 | let typeText = ""; 53 | 54 | switch (type) { 55 | case TooltipType.StepByStepExample: { 56 | icon.innerHTML = zapIconSVG; 57 | typeText = "learn"; 58 | typeContainer.classList.add("bg-learn"); 59 | 60 | break; 61 | } 62 | 63 | case TooltipType.UsageHint: { 64 | icon.innerHTML = lightBulbIconSVG; 65 | typeText = "hint"; 66 | typeContainer.classList.add("bg-hint"); 67 | 68 | break; 69 | } 70 | 71 | case TooltipType.RunnableExample: { 72 | icon.innerHTML = playIconSVG; 73 | typeText = "try"; 74 | typeContainer.classList.add("bg-try"); 75 | 76 | break; 77 | } 78 | } 79 | 80 | const typeElement = document.createElement("span"); 81 | typeElement.classList.add("row-type"); 82 | typeElement.innerHTML = typeText; 83 | 84 | const titleElement = document.createElement("span"); 85 | titleElement.classList.add("row-title"); 86 | titleElement.innerHTML = title; 87 | 88 | const chevronRightIcon = document.createElement("i"); 89 | chevronRightIcon.classList.add("row-chevron-right-icon"); 90 | chevronRightIcon.innerHTML = chevronRightIconSVG; 91 | 92 | typeContainer.appendChild(icon); 93 | typeContainer.appendChild(typeElement); 94 | textContainer.appendChild(typeContainer); 95 | textContainer.appendChild(titleElement); 96 | 97 | this.chevronElement = document.createElement("i"); 98 | this.chevronElement.classList.add("expand-collapse-button"); 99 | this.chevronElement.innerHTML = chevronDownIconSVG; 100 | 101 | headerContainer.addEventListener("click", () => { 102 | if (this.isOpen) { 103 | this.close(); 104 | 105 | this.sendUsageDuration(); 106 | } else { 107 | this.open(); 108 | onClick(); 109 | 110 | this.usageTime = Date.now(); 111 | } 112 | }); 113 | 114 | headerContainer.appendChild(textContainer); 115 | headerContainer.appendChild(this.chevronElement); 116 | } 117 | 118 | sendUsageDuration = () => { 119 | const duration = Date.now() - this.usageTime; 120 | 121 | if (duration > 1500) { 122 | Logger.Instance().queueEvent(new LogEvent(LogType.TooltipItemUsage, { type: this.type, duration })); 123 | 124 | this.usageTime = 0; 125 | } 126 | }; 127 | 128 | open() { 129 | this.contentContainer.style.maxHeight = this.contentContainer.scrollHeight + "px"; 130 | 131 | setTimeout(() => { 132 | this.contentContainer.style.maxHeight = "1000px"; 133 | }, 350); 134 | 135 | this.chevronElement.innerHTML = chevronUpIconSVG; 136 | 137 | this.accordion.rows.forEach((row) => { 138 | if (row !== this) { 139 | row.close(); 140 | } 141 | }); 142 | 143 | this.isOpen = true; 144 | } 145 | 146 | close() { 147 | this.contentContainer.style.maxHeight = "0px"; 148 | this.chevronElement.innerHTML = chevronDownIconSVG; 149 | 150 | this.isOpen = false; 151 | } 152 | 153 | onRemove() { 154 | this.sendUsageDuration(); 155 | } 156 | } 157 | 158 | export class Accordion { 159 | rows = new Array(); 160 | private id: string; 161 | container: HTMLDivElement; 162 | 163 | constructor(id: string) { 164 | this.id = id; 165 | 166 | this.container = document.createElement("div"); 167 | this.container.classList.add("accordion-group-container"); 168 | } 169 | 170 | addRow(type: TooltipType, title: string, content: HTMLDivElement, onClick: () => void = () => {}) { 171 | const id = this.id + "-" + this.rows.length; 172 | const row = new AccordionRow(this, id, type, title, content, onClick); 173 | this.rows.push(row); 174 | this.container.appendChild(row.element); 175 | } 176 | 177 | onRemove() { 178 | if (this?.rows) { 179 | this.rows.forEach((row) => { 180 | row.onRemove(); 181 | }); 182 | } 183 | } 184 | } 185 | 186 | const playIconSVG = ``; 187 | const lightBulbIconSVG = ``; 188 | const zapIconSVG = ``; 189 | const chevronUpIconSVG = ``; 190 | const chevronDownIconSVG = ``; 191 | const chevronRightIconSVG = ``; 192 | -------------------------------------------------------------------------------- /src/editor/action-filter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Expression, 3 | ForStatement, 4 | ListComma, 5 | Modifier, 6 | Statement, 7 | TypedEmptyExpr, 8 | ValueOperationExpr, 9 | VarAssignmentStmt, 10 | VariableReferenceExpr, 11 | VarOperationStmt, 12 | } from "../syntax-tree/ast"; 13 | import { InsertionType, TypeConversionRecord } from "../syntax-tree/consts"; 14 | import { Module } from "../syntax-tree/module"; 15 | import { Reference } from "../syntax-tree/scope"; 16 | import { getUserFriendlyType } from "../utilities/util"; 17 | import { ActionExecutor } from "./action-executor"; 18 | import { Actions, InsertActionType } from "./consts"; 19 | import { EventRouter } from "./event-router"; 20 | import { Context } from "./focus"; 21 | import { Validator } from "./validator"; 22 | 23 | export class ActionFilter { 24 | module: Module; 25 | 26 | constructor(module: Module) { 27 | this.module = module; 28 | } 29 | 30 | validateInsertions(): Map { 31 | const context = this.module.focus.getContext(); 32 | const validOptionMap: Map = new Map(); 33 | //need to know InsertionType in case we want to make any visual changes to those options in the suggestion menu 34 | 35 | // loop over all code-constructs and call their validateContext() + typeValidation() => insertionType 36 | // we are assuming that the action executor will calculate the insertionType again in the exectue() function 37 | for (const action of Actions.instance().actionsList) { 38 | validOptionMap.set( 39 | action.optionName, 40 | EditCodeAction.createDynamicEditCodeAction( 41 | action.optionName, 42 | action.cssId, 43 | action.getCodeFunction, 44 | action.insertActionType, 45 | action.insertData, 46 | action.validateAction(this.module.validator, context), 47 | action.terminatingChars, 48 | action.matchString, 49 | action.matchRegex, 50 | action.insertableTerminatingCharRegex 51 | ) 52 | ); 53 | } 54 | 55 | return validOptionMap; 56 | } 57 | 58 | validateEdits(): Map { 59 | // console.warn("validateEdits() is not implemented."); 60 | 61 | return new Map(); 62 | } 63 | 64 | validateVariableInsertions(): Map { 65 | const context = this.module.focus.getContext(); 66 | const validOptionMap: Map = new Map(); //