├── .eslintrc.js
├── .gitattributes
├── .github
└── workflows
│ └── pages.yml
├── .gitignore
├── .prettierrc
├── README.md
├── package.json
├── public
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── browserconfig.xml
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── index.html
├── manifest.json
├── mstile-150x150.png
├── safari-pinned-tab.svg
└── site.webmanifest
├── src
├── App.css
├── App.tsx
├── CreateEditShortcut.css
├── CreateEditShortcut.tsx
├── DownloadButton.tsx
├── DownloadModal.tsx
├── DownloadStatusSwitcher.tsx
├── ErrorBoundary.tsx
├── FileManager.ts
├── FilePane.css
├── FilePane.tsx
├── Key.ts
├── Modal.tsx
├── ModalContainer.tsx
├── SearchActions.css
├── SearchActions.tsx
├── UploadShortcutModal.css
├── UploadShortcutModal.tsx
├── Uploader.ts
├── ace
│ ├── mode-scpl.ts
│ └── scpl.iro
├── data
│ ├── ActionCategories.ts
│ └── ShortcutMeta.ts
├── img
│ ├── back.svg
│ ├── bg.jpg
│ ├── close.svg
│ ├── delete.svg
│ ├── delete_w.svg
│ ├── down.svg
│ ├── edit.svg
│ ├── edit_w.svg
│ ├── error.svg
│ ├── error_grey.svg
│ ├── error_w.svg
│ ├── error_x.svg
│ ├── file.png
│ ├── file_active.png
│ ├── file_error.png
│ ├── file_warning.png
│ ├── folder.png
│ ├── folder_error.png
│ ├── folder_warning.png
│ ├── glyph_sheet.png
│ ├── glyph_sheet_white.png
│ ├── icon.png
│ ├── import.png
│ ├── magic.svg
│ ├── magic_w.svg
│ ├── menu.svg
│ ├── new_file.png
│ ├── new_folder.png
│ ├── qr.svg
│ ├── right.svg
│ ├── search.svg
│ ├── settings.svg
│ ├── settings_w.svg
│ ├── shortcut-file.png
│ ├── shortcuts_grey.png
│ ├── success.svg
│ ├── upload.png
│ └── var-sheet.png
├── index.css
├── index.tsx
├── logo.svg
├── react-app-env.d.ts
├── serviceWorker.ts
└── testshortcut.json
└── tsconfig.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // http://eslint.org/docs/rules/
3 | //"parser": "esprima",
4 | // "parser": "espree",
5 | parser: "@typescript-eslint/parser",
6 | /*"ecmaFeatures": {
7 | "binaryLiterals": true, // enable binary literals
8 | "blockBindings": true, // enable let and const (aka block bindings)
9 | "defaultParams": true, // enable default function parameters
10 | "forOf": true, // enable for-of loops
11 | "generators": true, // enable generators
12 | "objectLiteralComputedProperties": true, // enable computed object literal property names
13 | "objectLiteralDuplicateProperties": true, // enable duplicate object literal properties in strict mode
14 | "objectLiteralShorthandMethods": true, // enable object literal shorthand methods
15 | "objectLiteralShorthandProperties": true, // enable object literal shorthand properties
16 | "experimentalObjectRestSpread": true, // enable {...a}
17 | "octalLiterals": true, // enable octal literals
18 | "regexUFlag": true, // enable the regular expression u flag
19 | "regexYFlag": true, // enable the regular expression y flag
20 | "templateStrings": true, // enable template strings
21 | "unicodeCodePointEscapes": true, // enable code point escapes
22 | "jsx": true, // enable JSX
23 | "es6": true,
24 | "ecmascript6": true
25 | },*/
26 | parserOptions: {
27 | ecmaVersion: 2018,
28 | sourceType: "module",
29 | ecmaFeatures: {
30 | jsx: true,
31 | modules: true
32 | },
33 | jsx: true
34 | },
35 |
36 | env: {
37 | browser: true, // browser global variables.
38 | node: true, // Node.js global variables and Node.js-specific rules.
39 | amd: false, // defines require() and define() as global variables as per the amd spec.
40 | mocha: false, // adds all of the Mocha testing global variables.
41 | jasmine: false, // adds all of the Jasmine testing global variables for version 1.3 and 2.0.
42 | phantomjs: false, // phantomjs global variables.
43 | jquery: false, // jquery global variables.
44 | prototypejs: false, // prototypejs global variables.
45 | shelljs: false, // shelljs global variables.
46 | es6: true
47 | },
48 |
49 | globals: {
50 | // e.g. "angular": true
51 | },
52 |
53 | plugins: [
54 | // e.g. "react" (must run `npm install eslint-plugin-react` first)
55 | "@typescript-eslint"
56 | ],
57 | extends: ["eslint:recommended"],
58 |
59 | rules: {
60 | ////////// Possible Errors //////////
61 |
62 | //"comma-dangle": 2, // disallow trailing commas in object literals
63 | "no-cond-assign": 0, // disallow assignment in conditional expressions
64 | "no-console": 1, // disallow use of console (off by default in the node environment)
65 | "no-constant-condition": 0, // disallow use of constant expressions in conditions
66 | "no-control-regex": 0, // disallow control characters in regular expressions
67 | "no-debugger": 0, // disallow use of debugger
68 | "no-dupe-keys": 0, // disallow duplicate keys when creating object literals
69 | "no-empty": 0, // disallow empty statements
70 | "no-empty-class": 0, // disallow the use of empty character classes in regular expressions
71 | "no-ex-assign": 0, // disallow assigning to the exception in a catch block
72 | "no-extra-boolean-cast": 0, // disallow double-negation boolean casts in a boolean context
73 | "no-extra-parens": 0, // disallow unnecessary parentheses (off by default)
74 | "no-extra-semi": 1, // disallow unnecessary semicolons
75 | "no-func-assign": 2, // disallow overwriting functions written as function declarations
76 | "no-inner-declarations": 0, // disallow function or variable declarations in nested blocks
77 | "no-invalid-regexp": 1, // disallow invalid regular expression strings in the RegExp constructor
78 | "no-irregular-whitespace": 1, // disallow irregular whitespace outside of strings and comments
79 | "no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression
80 | "no-obj-calls": 0, // disallow the use of object properties of the global object (Math and JSON) as functions
81 | "no-regex-spaces": 0, // disallow multiple spaces in a regular expression literal
82 | "no-sparse-arrays": 0, // disallow sparse arrays
83 | "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement
84 | "use-isnan": 0, // disallow comparisons with the value NaN
85 | "valid-jsdoc": 0, // Ensure JSDoc comments are valid (off by default)
86 | "valid-typeof": 0, // Ensure that the results of typeof are compared against a valid string
87 |
88 | ////////// Best Practices //////////
89 |
90 | "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default)
91 | complexity: 0, // specify the maximum cyclomatic complexity allowed in a program (off by default)
92 | "consistent-return": 0, // require return statements to either always or never specify values
93 | curly: [1], // specify curly brace conventions for all control statements
94 | "default-case": 1, // require default case in switch statements (off by default)
95 | "dot-notation": [1, { allowPattern: "(^[A-Z]|^[a-z]+_[a-z_]+$)" }],
96 | eqeqeq: ["error", "always", { null: "never" }], // require the use of === and !==
97 | "guard-for-in": 0, // make sure for-in loops have an if statement (off by default)
98 | "no-alert": 0, // disallow the use of alert, confirm, and prompt
99 | "no-caller": 0, // disallow use of arguments.caller or arguments.callee
100 | "no-div-regex": 0, // disallow division operators explicitly at beginning of regular expression (off by default)
101 | "no-else-return": 2, // disallow else after a return in an if (off by default)
102 | "no-empty-label": 0, // disallow use of labels for anything other then loops and switches
103 | "no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default)
104 | "no-eval": 2, // disallow use of eval()
105 | "no-extend-native": 0, // disallow adding to native types
106 | "no-extra-bind": 0, // disallow unnecessary function binding
107 | "no-fallthrough": 2, // disallow fallthrough of case statements
108 | "no-floating-decimal": 0, // disallow the use of leading or trailing decimal points in numeric literals (off by default)
109 | "no-implied-eval": 2, // disallow use of eval()-like methods
110 | "no-iterator": 0, // disallow usage of __iterator__ property
111 | "no-labels": 0, // disallow use of labeled statements
112 | "no-lone-blocks": 2, // disallow unnecessary nested blocks
113 | "no-loop-func": 0, // disallow creation of functions within loops
114 | "no-multi-spaces": 0, // disallow use of multiple spaces
115 | "no-multi-str": 0, // disallow use of multiline strings
116 | "no-native-reassign": 2, // disallow reassignments of native objects
117 | "no-new": 2, // disallow use of new operator when not part of the assignment or comparison
118 | "no-new-func": 2, // disallow use of new operator for Function object
119 | "no-new-wrappers": 2, // disallows creating new instances of String, Number, and Boolean
120 | "no-octal": 0, // disallow use of octal literals
121 | "no-octal-escape": 0, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
122 | "no-process-env": 0, // disallow use of process.env (off by default)
123 | "no-proto": 0, // disallow usage of __proto__ property
124 | "no-redeclare": 2, // disallow declaring the same variable more then once
125 | "no-return-assign": 0, // disallow use of assignment in return statement
126 | "no-script-url": 0, // disallow use of javascript: urls.
127 | "no-self-compare": 0, // disallow comparisons where both sides are exactly the same (off by default)
128 | "no-sequences": 0, // disallow use of comma operator
129 | "no-unused-expressions": 0, // disallow usage of expressions in statement position
130 | "no-void": 0, // disallow use of void operator (off by default)
131 | "no-warning-comments": 0, // disallow usage of configurable warning terms in comments, e.g. TODO or FIXME (off by default)
132 | "no-with": 0, // disallow use of the with statement
133 | radix: 2, // require use of the second argument for parseInt() (off by default)
134 | "vars-on-top": 0, // requires to declare all vars on top of their containing scope (off by default)
135 | "wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default)
136 | yoda: 1, // require or disallow Yoda conditions
137 |
138 | // "max-len": 1, // there is no fixer for this
139 |
140 | ////////// Strict Mode //////////
141 |
142 | "global-strict": 0, // (deprecated) require or disallow the "use strict" pragma in the global scope (off by default in the node environment)
143 | "no-extra-strict": 0, // (deprecated) disallow unnecessary use of "use strict"; when already in strict mode
144 | strict: 0, // controls location of Use Strict Directives
145 |
146 | ////////// Variables //////////
147 |
148 | "no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment)
149 | "no-delete-var": 2, // disallow deletion of variables
150 | "no-label-var": 1, // disallow labels that share a name with a variable
151 | "no-shadow": 0, // disallow declaration of variables already declared in the outer scope
152 | "no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments
153 | "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block
154 | "no-undef-init": 1, // disallow use of undefined when initializing variables
155 | "no-undefined": 0, // disallow use of undefined variable (off by default)
156 | "no-unused-vars": 0, // disallow declaration of variables that are not used in the code
157 | "no-use-before-define": 0, // disallow use of variables before they are defined
158 |
159 | ////////// Node.js //////////
160 |
161 | "handle-callback-err": 1, // enforces error handling in callbacks (off by default) (on by default in the node environment)
162 | "no-mixed-requires": [1, { grouping: true, allowCall: true }], // disallow mixing regular variable and require declarations (off by default) (on by default in the node environment)
163 | "no-new-require": 0, // disallow use of new operator with the require function (off by default) (on by default in the node environment)
164 | "no-path-concat": 2, // disallow string concatenation with __dirname and __filename (off by default) (on by default in the node environment)
165 | "no-process-exit": 0, // disallow process.exit() (on by default in the node environment)
166 | "no-restricted-modules": 0, // restrict usage of specified node modules (off by default)
167 | "no-sync": 0, // disallow use of synchronous methods (off by default)
168 |
169 | ////////// Stylistic Issues //////////
170 |
171 | //"brace-style": [1, "1tbs", {"allowSingleLine": true}], // enforce one true brace style (off by default)
172 | //"camelcase": 1, // require camel case names
173 | "comma-spacing": [1, { before: false, after: true }], // enforce spacing before and after comma
174 | "comma-style": 0, // enforce one true comma style (off by default)
175 | "consistent-this": 0, // enforces consistent naming when capturing the current execution context (off by default)
176 | "eol-last": 0, // enforce newline at the end of file, with no multiple empty lines
177 | "func-names": [1, "as-needed"], // require function expressions to have a name (off by default)
178 | "func-style": 0, // enforces use of function declarations or expressions (off by default)
179 | "key-spacing": [
180 | 1,
181 | { beforeColon: false, afterColon: true, mode: "strict" }
182 | ], // enforces spacing between keys and values in object literal properties
183 | "max-nested-callbacks": 0, // specify the maximum depth callbacks can be nested (off by default)
184 | // "new-cap": 1, // require a capital letter for constructors // Disabled because it applies to modules as well -.-
185 | "new-cap": 0,
186 | // "new-parens": 2,
187 | "new-parens": 0, // disallow the omission of parentheses when invoking a constructor with no arguments
188 | "no-array-constructor": 2, // disallow use of the Array constructor
189 | "no-inline-comments": 0, // disallow comments inline after code (off by default)
190 | "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default)
191 | "no-multiple-empty-lines": 0, // disallow multiple empty lines (off by default)
192 | "no-nested-ternary": 0, // disallow nested ternary expressions (off by default)
193 | "no-new-object": 1, // disallow use of the Object constructor
194 | "semi-spacing": [1, { before: false, after: true }], // disallow space before semicolon
195 | "no-spaced-func": 1, // disallow space between function identifier and application
196 | "no-ternary": 0, // disallow the use of ternary operators (off by default)
197 | "no-trailing-spaces": 0, // disallow trailing whitespace at the end of lines
198 | "no-underscore-dangle": 0, // disallow dangling underscores in identifiers
199 | "one-var": 0, // allow just one var statement per function (off by default)
200 | "operator-assignment": 2, // require assignment operator shorthand where possible or prohibit it entirely (off by default)
201 | "padded-blocks": [1, "never"], // enforce padding within blocks (off by default)
202 | "quote-props": [1, "as-needed"], // require quotes around object literal property names (off by default)
203 | quotes: [
204 | 1,
205 | "double",
206 | { avoidEscape: true, allowTemplateLiterals: true }
207 | ], // specify whether double or single quotes should be used
208 | "prefer-template": 1, // prefer template strings
209 | semi: [1, "always"], // require or disallow use of semicolons instead of ASI
210 | "sort-vars": 0, // sort variables within the same declaration block (off by default)
211 | "space-before-function-paren": [1, "never"], // require a space after certain keywords (off by default)
212 | "space-before-blocks": [1, "always"], // require or disallow space before blocks (off by default)
213 | "space-in-parens": 0, // require or disallow spaces inside parentheses (off by default)
214 | "space-unary-ops": 0, // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default)
215 | "prefer-const": 1,
216 | complexity: 1,
217 | "no-mixed-spaces-and-tabs": 0,
218 |
219 | ////////// ECMAScript 6 //////////
220 |
221 | "no-var": 2, // require let or const instead of var (off by default)
222 | "generator-star-spacing": ["error", { before: true, after: false }] // enforce the position of the * in generator functions (off by default)
223 | }
224 | };
225 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | docs/**/* linguist-generated=true
2 |
--------------------------------------------------------------------------------
/.github/workflows/pages.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | branches:
5 | - no-files
6 | jobs:
7 | build-and-deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout 🛎️
11 | uses: actions/checkout@v2.3.1
12 | with:
13 | fetch-depth: 1
14 |
15 | - name: Install and Build 🔧
16 | run: |
17 | yarn install
18 | yarn add sass
19 | env node_env=PRODUCTION yarn build
20 | - name: Deploy 🚀
21 | uses: JamesIves/github-pages-deploy-action@4.0.0
22 | with:
23 | branch: gh-pages # The branch the action should deploy to.
24 | folder: docs # The folder the action should deploy.
25 | clean: true # Automatically remove deleted files from the deploy branch
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /docs
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # junk folder
27 | /junk
28 | package-lock.json
29 |
30 |
31 | yarn.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 80,
3 | "tabWidth": 4,
4 | "useTabs": true,
5 | "semi": true,
6 | "singleQuote": false,
7 | "trailingComma": "none",
8 | "bracketSpacing": true,
9 | "jsxBracketSameLine": false,
10 | "arrowParens": "avoid",
11 | "proseWrap": "always",
12 | "overrides": [
13 | {
14 | "files": ".prettierrc",
15 | "options": { "parser": "json" }
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # scpl editor
2 |
3 | 
4 |
5 | Shortcuts are previewed using [shortcut-preview](https://github.com/xalien95/shortcut-preview).
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "scpl-editor",
3 | "version": "0.1.0",
4 | "private": true,
5 | "homepage": "https://scpl.dev/",
6 | "dependencies": {
7 | "browserfs": "^1.4.3",
8 | "pretty-bytes": "^5.1.0",
9 | "qrcode": "^1.3.3",
10 | "react": "^16.8.4",
11 | "react-ace": "^6.4.0",
12 | "react-dom": "^16.8.4",
13 | "react-dropzone": "^10.0.5",
14 | "react-helmet": "^5.2.0",
15 | "react-markdown": "^4.0.8",
16 | "react-portal": "^4.2.0",
17 | "react-scripts": "2.1.8",
18 | "scpl": "^1.9.3",
19 | "shortcut-preview": "pfgithub/shortcut-preview",
20 | "typescript": "3.3.3333",
21 | "yarn": "^1.17.3"
22 | },
23 | "scripts": {
24 | "start": "react-scripts start",
25 | "build": "react-scripts build && rm -rf docs && mv build docs && echo \"editor.scpl.dev\" > docs/CNAME",
26 | "test": "react-scripts test",
27 | "eject": "react-scripts eject",
28 | "format": "prettier --write src/***.{css,tsx,ts,json} .prettierrc .eslintrc.js package.json"
29 | },
30 | "eslintConfig": {
31 | "extends": "react-app"
32 | },
33 | "browserslist": [
34 | ">0.2%",
35 | "not dead",
36 | "not ie <= 11",
37 | "not op_mini all"
38 | ],
39 | "devDependencies": {
40 | "@types/jest": "24.0.11",
41 | "@types/node": "11.11.0",
42 | "@types/pretty-bytes": "^5.1.0",
43 | "@types/qrcode": "^1.3.3",
44 | "@types/react": "16.8.7",
45 | "@types/react-dom": "16.8.2",
46 | "@types/react-helmet": "^5.0.8",
47 | "@types/react-portal": "^4.0.2",
48 | "@typescript-eslint/eslint-plugin": "^1.4.2",
49 | "@typescript-eslint/parser": "^1.4.2",
50 | "onchange": "^5.2.0",
51 | "prettier": "^1.17.0"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #1e1f57
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
30 | ScPL Editor
31 |
32 |
33 |
34 |
35 |
36 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/public/mstile-150x150.png
--------------------------------------------------------------------------------
/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
80 |
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ScPL Editor",
3 | "short_name": "ScPL Editor",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#1e1f57",
17 | "background_color": "#1e1f57",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { parse, PositionedError } from "scpl";
3 | import "./App.css";
4 | // import {Helmet} from "react-helmet";
5 |
6 | import testshortcut from "./testshortcut.json";
7 |
8 | import ace from "brace";
9 | import "./ace/mode-scpl";
10 | import "brace/theme/chrome";
11 | import "brace/ext/language_tools";
12 | import "brace/ext/searchbox";
13 | import AceEditor from "react-ace";
14 |
15 | import { FilePane } from "./FilePane";
16 | import { SearchActions } from "./SearchActions";
17 | import { DownloadModal } from "./DownloadModal";
18 | import { keys } from "./Key";
19 | import { UploadShortcutModal } from "./UploadShortcutModal";
20 | import { ErrorBoundary } from "./ErrorBoundary";
21 |
22 | import ShortcutPreview from "shortcut-preview";
23 |
24 | let timeout: NodeJS.Timeout;
25 |
26 | const Range = ace.acequire("ace/range").Range;
27 |
28 | const langTools = ace.acequire("ace/ext/language_tools");
29 | const findAndReplace = ace.acequire("ace/ext/searchbox");
30 |
31 | const rhymeCompleter = {
32 | getCompletions: (
33 | editor: ace.Editor,
34 | session: ace.IEditSession,
35 | pos: ace.Position,
36 | prefix: string,
37 | callback: (
38 | thing: null,
39 | completions: {
40 | name: string;
41 | value: string;
42 | score: number;
43 | meta: string;
44 | }[]
45 | ) => void
46 | ) => {
47 | if (prefix.length === 0) {
48 | callback(null, []);
49 | return;
50 | }
51 | const variables = session.getValue().match(/(v|mv):([A-Za-z0-9]+)/g);
52 | console.log(variables);
53 | if (!variables) {
54 | callback(null, []);
55 | return;
56 | }
57 | callback(
58 | null,
59 | variables.map(v => ({
60 | name: v,
61 | value: v,
62 | score: 25,
63 | meta: v
64 | }))
65 | );
66 | }
67 | };
68 | langTools.addCompleter(rhymeCompleter);
69 |
70 | const hotkey =
71 | window.navigator.platform === "MacIntel" ||
72 | window.navigator.platform.includes("Win")
73 | ? "⌘"
74 | : "^";
75 |
76 | class MaybeUpdate extends Component<{ shouldUpdate: boolean }, {}> {
77 | shouldComponentUpdate(nextProps: { shouldUpdate: boolean }) {
78 | return nextProps.shouldUpdate;
79 | }
80 | render() {
81 | return this.props.children;
82 | }
83 | }
84 |
85 | class App extends Component<
86 | {},
87 | {
88 | fileValue: string;
89 | shortcutData: any;
90 | shortcutDownload: Buffer | undefined;
91 | annotations: Array;
92 | markers: Array<{
93 | startRow: number;
94 | endRow: number;
95 | startCol: number;
96 | endCol: number;
97 | className: string;
98 | type: string;
99 | }>;
100 | errors: Array<{
101 | startRow: number;
102 | endRow: number;
103 | startCol: number;
104 | endCol: number;
105 | message: string;
106 | }>;
107 | loading: boolean;
108 | took: { waitedFor: number; convertedIn: number };
109 | mobileFilemenu: boolean;
110 | openDownload: boolean;
111 | showPreview: boolean;
112 | showPreviewFullscreen: boolean;
113 | showUploadShortcutModal: boolean;
114 | tabs: { filename: string; active: boolean }[];
115 | files: {
116 | [filename: string]: string;
117 | };
118 | }
119 | > {
120 | reactAceComponentRef: React.RefObject;
121 | constructor(props: Readonly<{}>) {
122 | super(props);
123 | this.state = {
124 | fileValue: "...",
125 | shortcutData: testshortcut,
126 | shortcutDownload: undefined,
127 | annotations: [],
128 | markers: [],
129 | loading: false,
130 | took: { waitedFor: 0, convertedIn: 0 },
131 | mobileFilemenu: false,
132 | openDownload: false,
133 | showPreview: false,
134 | showPreviewFullscreen: false,
135 | showUploadShortcutModal: false,
136 | errors: [],
137 | tabs: [{ filename: "download.scpl", active: true }],
138 | files: {
139 | "download.scpl": `ShowResult "Hello ScPL"
140 | ChooseFromMenu "ScPL Editor" items=["Getting Started", "View Documentation"]
141 | Case "Getting Started"
142 | URL "https://docs.scpl.dev/gettingstarted"
143 | Case "View Documentation"
144 | URL "https://docs.scpl.dev/"
145 | End Menu
146 | OpenURLs`,
147 | "other.scpl": `ShowResult "ScPL"`,
148 | urlquery:
149 | new URLSearchParams(window.location.search).get("scpl") ||
150 | ""
151 | }
152 | };
153 | this.reactAceComponentRef = React.createRef();
154 | }
155 | componentDidMount() {}
156 | componentWillMount() {
157 | const urlParams = new URLSearchParams(window.location.search);
158 | this.onChange(
159 | new URLSearchParams(window.location.search).get("scpl") ||
160 | `ShowResult "Hello ScPL"
161 | ChooseFromMenu "ScPL Editor" items=["Getting Started", "View Documentation"]
162 | Case "Getting Started"
163 | URL "https://docs.scpl.dev/gettingstarted"
164 | Case "View Documentation"
165 | URL "https://docs.scpl.dev/"
166 | End Menu
167 | OpenURLs`
168 | );
169 | }
170 | render() {
171 | return (
172 | {
174 | if (keys.save(e)) {
175 | alert("save");
176 | }
177 | if (keys.export(e)) {
178 | this.setState({ openDownload: true });
179 | }
180 | }}
181 | >
182 |
183 |
Drop file anywhere to upload
184 |
185 | {this.state.openDownload ? (
186 |
{
188 | this.setState({ openDownload: false });
189 | }}
190 | filename={
191 | this.state.shortcutData._filename ||
192 | "download.shortcut"
193 | }
194 | file={this.state.shortcutDownload}
195 | />
196 | ) : null}
197 | {this.state.showUploadShortcutModal ? (
198 |
200 | this.setState({ showUploadShortcutModal: false })
201 | }
202 | onResult={result => {
203 | this.setState({ showUploadShortcutModal: false });
204 | this.onChange(result);
205 | }}
206 | />
207 | ) : null}
208 |
209 |
210 |
216 | this.setState({
217 | mobileFilemenu: !this.state.mobileFilemenu
218 | })
219 | }
220 | />
221 |
222 |
ScPL Editor
223 |
224 |
225 | -
226 | File
227 |
254 |
255 |
256 |
257 |
258 |
259 | -
260 | Edit
261 |
355 |
356 |
357 |
358 |
359 |
360 | -
361 | Help
362 |
388 |
389 |
390 |
391 |
392 |
393 | {
395 | console.log("insert", text);
396 | const reactAceComponent = this
397 | .reactAceComponentRef.current;
398 | const editor = (reactAceComponent as any)
399 | .editor as ace.Editor;
400 | editor.session.insert(
401 | editor.getCursorPosition(),
402 | text
403 | );
404 | console.log(
405 | editor.session.getValue(),
406 | text,
407 | editor.getCursorPosition()
408 | );
409 | this.setState({
410 | fileValue: editor.session.getValue()
411 | });
412 | }}
413 | />
414 |
415 |
416 |
417 |
418 | {this.state.shortcutData &&
419 | this.state.shortcutData[0].WFWorkflowActions
420 | .length}{" "}
421 | action
422 | {this.state.shortcutData &&
423 | this.state.shortcutData[0].WFWorkflowActions
424 | .length === 1
425 | ? ""
426 | : "s"}
427 |
428 |
429 |
430 |
445 |
449 | this.setState({
450 | openDownload: true
451 | })
452 | }
453 | >
454 |
Export
455 |
456 |
457 |
458 |
459 |
464 | this.onChange(file)}
508 | />
509 |
510 |
511 |
512 | {this.state.errors.map(err => (
513 |
514 |
515 | {err.message}
516 |
517 |
518 |
525 |
526 |
527 | ))}
528 |
529 |
530 | {this.state.tabs.length > 1 ? (
531 |
532 | {this.state.tabs.map(tab => (
533 |
538 |
539 | ×
540 |
541 |
542 | {tab.filename}
543 |
544 |
545 | ))}
546 |
547 | ) : null}
548 |
549 |
563 |
564 |
578 |
579 | Converted in {this.state.took.convertedIn} ms.
580 |
581 | {this.state.showPreview && this.state.shortcutData ? (
582 |
(
584 |
585 |
586 | A fatal error occured in
587 | shortcut-preview. The error is{" "}
588 | {err.toString()}
589 |
590 |
591 | )}
592 | >
593 |
595 | this.onActionSelect(data)
596 | }
597 | data={this.state.shortcutData}
598 | />
599 |
600 | ) : null}
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 | {!this.state.showPreview ? (
620 |
621 |
622 |
623 | There are too many actions to render a
624 | preview automatically.
625 |
626 |
636 |
637 |
638 | ) : null}
639 | {!this.state.shortcutData ? (
640 |
641 |
642 |
643 | Failed to render because of code error.
644 |
645 |
646 |
647 | ) : null}
648 |
649 |
650 |
651 | );
652 | }
653 | onActionSelect(data: { type: "action" | "parameter"; actionData: any }) {
654 | if (data.actionData.SCPLData) {
655 | const scpldata = data.actionData.SCPLData;
656 |
657 | const reactAceComponent = this.reactAceComponentRef.current;
658 | if (!reactAceComponent) {
659 | return;
660 | }
661 | const editor = (reactAceComponent as any).editor as ace.Editor;
662 | // .Position.start|end
663 | const line = scpldata.Position.start[0];
664 | const col = scpldata.Position.start[1] - 1;
665 | editor.gotoLine(line, col, true);
666 | editor.selection.setRange(
667 | new Range(
668 | scpldata.Position.start[0] - 1,
669 | scpldata.Position.start[1] - 1,
670 | scpldata.Position.end[0] - 1,
671 | scpldata.Position.end[1] - 1
672 | ),
673 | true
674 | );
675 | }
676 | }
677 | getAce(): ace.Editor {
678 | const reactAceComponent = this.reactAceComponentRef.current;
679 | if (!reactAceComponent) {
680 | throw new Error("No reactacecomponent");
681 | }
682 | const editor = (reactAceComponent as any).editor as ace.Editor;
683 | return editor;
684 | }
685 | jumpToError(d: {
686 | startCol: number;
687 | startRow: number;
688 | endCol: number;
689 | endRow: number;
690 | }) {
691 | const reactAceComponent = this.reactAceComponentRef.current;
692 | if (!reactAceComponent) {
693 | return;
694 | }
695 | const editor = (reactAceComponent as any).editor as ace.Editor;
696 | const line = d.startRow;
697 | const col = d.startCol - 1;
698 | editor.gotoLine(line, col, true);
699 | editor.selection.setRange(
700 | new Range(
701 | d.startRow - 1,
702 | d.startCol - 1,
703 | d.endRow - 1,
704 | d.endCol - 1
705 | ),
706 | true
707 | );
708 | }
709 | onChange(text: string) {
710 | // Enable navigation prompt
711 | window.onbeforeunload = () => {
712 | return true;
713 | };
714 | const willWaitFor = Math.max(this.state.took.convertedIn * 4, 100);
715 | if (!this.state.loading) {
716 | this.setState({
717 | showPreview: this.state.took.convertedIn < 50,
718 | fileValue: text,
719 | loading: true
720 | });
721 | }
722 | if (timeout) {
723 | clearTimeout(timeout);
724 | }
725 | timeout = setTimeout(
726 | () => this.onChangeLimited(text, willWaitFor),
727 | willWaitFor
728 | );
729 | }
730 | onChangeLimited(text: string, waitedFor: number) {
731 | // parse
732 | let output: {
733 | shortcutjson: any;
734 | shortcutplist: Buffer;
735 | warnings: PositionedError[];
736 | };
737 | const annotationFromError = (
738 | err: PositionedError,
739 | isWarning: boolean
740 | ) => {
741 | return {
742 | row: err.start[0] - 1,
743 | column: err.start[1] - 1,
744 | text: err.message, // Or the Json reply from the parser
745 | type: isWarning ? "warning" : "error"
746 | };
747 | };
748 | const markerFromError = (er: PositionedError, isWarning: boolean) => {
749 | return {
750 | startRow: er.start[0] - 1,
751 | endRow: er.end[0] - 1,
752 | startCol: er.start[1] - 1,
753 | endCol: er.end[1] - 1,
754 | className: `ace_active-line ${isWarning ? "warning" : "error"}`,
755 | type: "background"
756 | };
757 | };
758 | const errorBannerFromError = (
759 | er: PositionedError,
760 | isWarning: boolean
761 | ) => {
762 | return {
763 | startRow: er.start[0],
764 | endRow: er.end[0],
765 | startCol: er.start[1],
766 | endCol: er.end[1],
767 | message: er.message,
768 | type: isWarning ? "warning" : "error"
769 | };
770 | };
771 | const startTime = new Date().getTime();
772 | try {
773 | output = parse(text, {
774 | make: ["shortcutjson", "shortcutplist"],
775 | useWarnings: true
776 | });
777 | } catch (er) {
778 | console.log(er.message); //eslint-disable-line no-console
779 | if (!(er instanceof PositionedError)) {
780 | throw er;
781 | }
782 | this.setState({
783 | fileValue: text,
784 | loading: false,
785 | shortcutDownload: undefined,
786 | shortcutData: undefined,
787 | took: {
788 | waitedFor: waitedFor,
789 | convertedIn: new Date().getTime() - startTime
790 | },
791 | annotations: [annotationFromError(er, false)],
792 | markers: [markerFromError(er, false)],
793 | errors: [errorBannerFromError(er, false)]
794 | });
795 | return;
796 | }
797 | const { shortcutjson, shortcutplist } = output;
798 | this.setState({
799 | fileValue: text,
800 | took: {
801 | waitedFor: waitedFor,
802 | convertedIn: new Date().getTime() - startTime
803 | },
804 | loading: false,
805 | shortcutData: shortcutjson,
806 | annotations: [
807 | ...output.warnings.map(warn => annotationFromError(warn, false))
808 | ],
809 | markers: [
810 | ...output.warnings.map(warn => markerFromError(warn, false))
811 | ],
812 | errors: [
813 | ...output.warnings.map(warn =>
814 | errorBannerFromError(warn, false)
815 | )
816 | ],
817 | shortcutDownload: shortcutplist
818 | });
819 | }
820 | }
821 |
822 | export default App;
823 |
--------------------------------------------------------------------------------
/src/CreateEditShortcut.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | import {
4 | GlyphName,
5 | objectGlyphs,
6 | peopleGlyphs,
7 | symbolGlyphs,
8 | ColorName,
9 | colors
10 | } from "./data/ShortcutMeta";
11 | import { ModalContainer } from "./ModalContainer";
12 |
13 | import "./CreateEditShortcut.css";
14 |
15 | type CreateEditShortcutProps = {
16 | onCancel: () => void;
17 | onResult: (name: string, color: undefined, glyph: undefined) => void;
18 | };
19 |
20 | export class CreateEditShortcut extends Component<
21 | CreateEditShortcutProps,
22 | { chosenGlyph: GlyphName; chosenColor: ColorName; mode: "color" | "glyph" }
23 | > {
24 | constructor(props: Readonly) {
25 | super(props);
26 | this.state = {
27 | chosenGlyph: "magicwand",
28 | chosenColor: "purple",
29 | mode: "color"
30 | };
31 | }
32 | render() {
33 | return (
34 | this.props.onCancel()}>
35 |
40 |
this.props.onCancel()}
44 | >
45 | Cancel
46 |
47 |
New ScPL File
48 |
49 |
60 |
61 |
67 |
72 | Name is required.
73 |
74 |
75 |
76 |
77 |
78 |
87 |
96 |
97 |
98 |
99 |
108 |
109 |
Objects
110 |
111 | {objectGlyphs.map(id => (
112 |
113 |
122 | this.setState({
123 | chosenGlyph: id
124 | })
125 | }
126 | />
127 |
128 |
129 | ))}
130 |
131 |
132 |
133 |
People
134 |
135 | {peopleGlyphs.map(id => (
136 |
137 |
146 | this.setState({
147 | chosenGlyph: id
148 | })
149 | }
150 | />
151 |
152 |
153 | ))}
154 |
155 |
156 |
157 |
Symbols
158 |
159 | {symbolGlyphs.map(id => (
160 |
161 |
170 | this.setState({
171 | chosenGlyph: id
172 | })
173 | }
174 | />
175 |
176 |
177 | ))}
178 |
179 |
180 |
181 |
182 |
190 |
191 | {colors.map(color => (
192 |
193 |
201 | this.setState({
202 | chosenColor: color
203 | })
204 | }
205 | />
206 |
207 |
208 | ))}
209 |
210 |
211 |
212 |
213 | Create
214 |
215 |
216 |
217 | );
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/DownloadButton.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | export class DownloadButton extends Component<
4 | { filename: string; file: Buffer | undefined },
5 | {}
6 | > {
7 | // from https://github.com/axetroy/react-download
8 | render() {
9 | return (
10 | this.onClick(e)}
14 | >
15 | {this.props.children}
16 |
17 | );
18 | }
19 | fakeClick(obj: HTMLAnchorElement) {
20 | const ev = document.createEvent("MouseEvents");
21 | ev.initMouseEvent(
22 | "click",
23 | true,
24 | false,
25 | window,
26 | 0,
27 | 0,
28 | 0,
29 | 0,
30 | 0,
31 | false,
32 | false,
33 | false,
34 | false,
35 | 0,
36 | null
37 | );
38 | obj.dispatchEvent(ev);
39 | }
40 | exportRaw(name: string, data: Buffer) {
41 | const urlObject = window.URL || (window as any).webkitURL || window;
42 | const export_blob = new Blob([data]);
43 |
44 | if ("msSaveBlob" in navigator) {
45 | // Prefer msSaveBlob if available - Edge supports a[download] but
46 | // ignores the filename provided, using the blob UUID instead.
47 | // msSaveBlob will respect the provided filename
48 | navigator.msSaveBlob(export_blob, name);
49 | } else if ("download" in HTMLAnchorElement.prototype) {
50 | const save_link = document.createElementNS(
51 | "http://www.w3.org/1999/xhtml",
52 | "a"
53 | ) as HTMLAnchorElement;
54 | save_link.href = urlObject.createObjectURL(export_blob);
55 | save_link.download = name;
56 | this.fakeClick(save_link);
57 | } else {
58 | alert(
59 | "Downloading shortcuts is not available on this browser yet :(\nIt should be implemented within a few days from a few days from a while from now."
60 | );
61 | }
62 | }
63 | onClick(_e: React.MouseEvent) {
64 | if (!this.props.file) {
65 | return;
66 | }
67 | this.exportRaw(this.props.filename, this.props.file);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/DownloadModal.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | import * as QRCode from "qrcode";
4 |
5 | import { ModalContainer } from "./ModalContainer";
6 | import { DownloadButton } from "./DownloadButton";
7 |
8 | import prettyBytes from "pretty-bytes";
9 |
10 | import shortcutDownloadPreviewIcon from "./img/shortcut-file.png";
11 | import { uploadShortcut } from "./Uploader";
12 | import { DownloadStatusSwitcher, UploadStatus } from "./DownloadStatusSwitcher";
13 |
14 | import "./CreateEditShortcut.css";
15 |
16 | export class ShortcutDownloadPreviewButton extends Component<
17 | { filename: string; filesize: number },
18 | {}
19 | > {
20 | render() {
21 | return (
22 |
23 |

24 |
{this.props.filename}
25 |
26 | {prettyBytes(this.props.filesize)}
27 |
28 |
29 | );
30 | }
31 | }
32 |
33 | type DownloadModalProps = {
34 | onCancel: () => void;
35 | filename: string;
36 | file: Buffer | undefined;
37 | };
38 |
39 | export class DownloadModal extends Component {
40 | constructor(props: Readonly) {
41 | super(props);
42 | this.state = { uploadStatus: "None" };
43 | }
44 | componentWillReceiveProps(nextProps: Readonly) {
45 | if (nextProps.file !== this.props.file) {
46 | this.setState({ uploadStatus: "None" });
47 | }
48 | }
49 | async uploadFile() {
50 | if (!this.props.file) {
51 | this.setState({
52 | uploadStatus: "Error",
53 | uploadError: "No file. Make sure your shortcut has no errors."
54 | });
55 | return;
56 | }
57 | this.setState({
58 | uploadStatus: "Uploading"
59 | });
60 | const uploadResult = await uploadShortcut(
61 | this.props.file,
62 | this.props.filename
63 | );
64 | if (uploadResult.result === "error") {
65 | this.setState({
66 | uploadStatus: "Error",
67 | uploadError: uploadResult.message
68 | });
69 | return;
70 | }
71 | const qrcode = await QRCode.toDataURL(uploadResult.url);
72 | this.setState({
73 | uploadStatus: "URL",
74 | uploadedURL: uploadResult.url,
75 | qrcode: qrcode
76 | });
77 | }
78 | render() {
79 | return (
80 | this.props.onCancel()}>
81 | e.stopPropagation()}
88 | >
89 |
Export Shortcut File
90 | {this.props.file ? (
91 |
92 |
93 | {"download" in HTMLAnchorElement.prototype ? (
94 |
98 |
104 |
105 | ) : (
106 |
this.uploadFile()}
109 | detailsMsg={
110 |
111 | To download on this browser,
112 | ScPL editor will upload your
113 | shortcut to{" "}
114 |
119 | file.io
120 |
121 | .
122 |
123 | }
124 | uploadAction="Create Download Link"
125 | >
126 |
133 |
139 |
140 |
141 | )}
142 |
143 |
or
144 |
145 |
this.uploadFile()}
148 | detailsMsg={
149 |
150 |
QR codes must be
151 | scanned using{" "}
152 |
157 | This Shortcut
158 |
159 | {" "}due to new security
160 | restrictions in iOS 13.{" "}
161 | To generate a QR code, ScPL editor
162 | will upload your shortcut to{" "}
163 |
168 | file.io
169 |
170 | .
171 |
172 | }
173 | uploadAction="Generate QR Code"
174 | additionalButtonClasses="qr-btn"
175 | >
176 |
177 |
178 | Add to your library via QR Code:
179 |
180 |
191 |
192 | Open your Camera app and point it
193 | steady for 2-3 seconds at this QR
194 | Code.
195 |
196 |
197 |
198 |
199 |
200 | ) : (
201 |
202 |
Your code seems to have some errors.
203 |
204 | )}
205 |
206 |
this.props.onCancel()}
210 | >
211 | Done
212 |
213 |
214 |
215 | );
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/src/DownloadStatusSwitcher.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | export type UploadStatus =
4 | | {
5 | uploadStatus: "None" | "Uploading";
6 | }
7 | | {
8 | uploadStatus: "URL";
9 | uploadedURL: string;
10 | qrcode: string;
11 | }
12 | | {
13 | uploadStatus: "Error";
14 | uploadError: string;
15 | };
16 |
17 | export class DownloadStatusSwitcher extends Component<{
18 | status: UploadStatus;
19 | requestUpload: () => void;
20 | detailsMsg: React.ReactNode;
21 | uploadAction: string;
22 | additionalButtonClasses?: string;
23 | }> {
24 | render() {
25 | switch (this.props.status.uploadStatus) {
26 | case "None":
27 | return (
28 |
29 |
30 |
37 |
{this.props.detailsMsg}
38 |
39 | );
40 | case "URL":
41 | return this.props.children;
42 | case "Uploading":
43 | return (
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
Uploading...
61 |
62 |
63 | );
64 | case "Error":
65 | return (
66 |
67 |
Error uploading: {this.props.status.uploadError}
68 |
69 | );
70 | default:
71 | return (
72 |
73 |
Something bad happened.
74 |
75 | );
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | type ErrorBoundaryProps = {
4 | errorDisplay: (error: Error) => React.ReactNode;
5 | };
6 | export class ErrorBoundary extends Component<
7 | ErrorBoundaryProps,
8 | { error: Error | undefined }
9 | > {
10 | constructor(props: Readonly) {
11 | super(props);
12 | this.state = { error: undefined };
13 | }
14 | componentDidCatch(error: Error) {
15 | this.setState({ error: error });
16 | }
17 | render() {
18 | if (this.state.error) {
19 | console.log(this.state.error);
20 | return this.props.errorDisplay(this.state.error);
21 | }
22 | return this.props.children;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/FileManager.ts:
--------------------------------------------------------------------------------
1 | export class FileManager {
2 | constructor() {}
3 | listItems() {
4 | // returns a list of files and folders
5 | }
6 | createFolder() {}
7 | saveFile(path: string, data: string) {
8 | // saves the file at the path
9 | }
10 | loadFile(path: string, data: string) {}
11 | renameFile(path: string, newPath: string) {}
12 | }
13 |
--------------------------------------------------------------------------------
/src/FilePane.css:
--------------------------------------------------------------------------------
1 | /* File pane */
2 | .file-pane {
3 | width: 300px;
4 | background-color: #fff;
5 | border-right: 0.5px solid #c7c7cc;
6 | transition: 0.1s left ease-out;
7 | z-index: 100;
8 | padding-bottom: 20px;
9 | overflow-x: hidden;
10 | overflow-y: auto;
11 | height: stretch;
12 | }
13 | .file-pane input {
14 | width: 90%;
15 | margin-left: 15px;
16 | }
17 | .file-pane h2 {
18 | margin: 20px 15px;
19 | }
20 | /* Files Navigation */
21 | .files-header {
22 | position: sticky;
23 | top: 0;
24 | background-color: #fafafa;
25 | height: 55px;
26 | border-bottom: 1px solid #ddd;
27 | margin-bottom: 10px;
28 | padding: 15px 0;
29 | box-shadow: 0px -5px 20px rgba(0, 0, 0, 0.15);
30 | }
31 | .file-btns {
32 | display: grid;
33 | grid-template-columns: 1fr 1fr 1fr;
34 | padding: 0 10px;
35 | }
36 | .file-btn {
37 | font-size: 15px;
38 | padding: 13px 0;
39 | width: 100%;
40 | width: stretch;
41 | padding-left: 30px;
42 | font-weight: normal;
43 | outline: none;
44 | }
45 | .upload-btn {
46 | background: #eee url(img/upload.png) center / 25px no-repeat;
47 | }
48 | .large-upload-btn {
49 | background: #eee url(img/upload.png) 22% center / 25px no-repeat;
50 | padding-left: 10%;
51 | }
52 | .new-btn {
53 | background: #eee url(img/new_file.png) center / 25px no-repeat;
54 | }
55 | .newf-btn {
56 | background: #eee url(img/new_folder.png) center / 25px no-repeat;
57 | }
58 | /* File list */
59 | .file-list li {
60 | padding: 8px 10px;
61 | padding-left: 25px;
62 | cursor: pointer;
63 | user-select: none;
64 | transition: 0.3s background-color;
65 | }
66 | .file-list li:hover:not(.open-folder) {
67 | background-color: #e8f6fe !important;
68 | }
69 | .file-list li:active:not(.open-folder) {
70 | background-color: #daeffc !important;
71 | }
72 | .file-list li.open-folder > ul > li {
73 | width: 94%;
74 | }
75 | .file-list ul {
76 | list-style-type: none;
77 | padding: 0;
78 | margin: 0;
79 | }
80 | .file-list ul ul {
81 | border-left: 1px solid #ddd;
82 | margin: 5px 10px;
83 | }
84 | .list-item-file > div,
85 | .list-item-folder > div {
86 | padding-left: 27px;
87 | font-size: 14px;
88 | }
89 | .item-name {
90 | width: 70%;
91 | height: 18px;
92 | white-space: nowrap;
93 | overflow: hidden;
94 | text-overflow: ellipsis;
95 | display: inline-block;
96 | margin-top: 3px;
97 | }
98 | .item-name .load {
99 | display: none;
100 | }
101 | .file-list ul li.open-folder ul li > div > .item-name {
102 | width: 60%;
103 | }
104 | .file-list ul li.open-folder ul li.open-folder ul > li > div > .item-name {
105 | width: 50%;
106 | }
107 | /* File items */
108 | .list-item-file > div {
109 | background: url(img/file.png) 2px center / 18px no-repeat;
110 | }
111 | .file-list li.list-item-file.active {
112 | background-color: #007aff20;
113 | font-weight: 500;
114 | }
115 | .file-list li.list-item-file.active:hover {
116 | background-color: #007aff10 !important;
117 | }
118 | .file-list li.list-item-file.active > div {
119 | background-image: url(img/file_active.png);
120 | }
121 | /* uploading state */
122 | .list-item-file.uploading {
123 | background: none;
124 | pointer-events: none;
125 | user-select: none;
126 | color: #777;
127 | }
128 | .list-item-file.uploading .action-btns {
129 | opacity: 0.5;
130 | }
131 | .list-item-file.uploading > div {
132 | background: none;
133 | }
134 | .list-item-file.uploading .item-name .load {
135 | display: inline-block;
136 | position: absolute;
137 | margin-left: -25px;
138 | margin-top: 2px;
139 | width: 10px;
140 | height: 10px;
141 | }
142 | /* Folder items */
143 | .list-item-folder > div {
144 | background: url(img/folder.png) left center / 20px no-repeat;
145 | }
146 | .file-list li.list-item-folder > ul {
147 | display: none;
148 | transform-origin: top center;
149 | transition: 0.1s left ease-out;
150 | transform: scale(1, 0);
151 | }
152 | .file-list li.list-item-folder {
153 | background: url(img/right.svg) 10px 15px / 10px no-repeat;
154 | }
155 | .file-list li.list-item-folder.open-folder {
156 | background: url(img/down.svg) 10px 15px / 10px no-repeat;
157 | padding-bottom: 0;
158 | }
159 | .file-list li.list-item-folder.open-folder > ul {
160 | display: block;
161 | animation: scaleIn 0.1s;
162 | transform: scale(1, 1);
163 | }
164 | .list-item-folder.open-folder > div {
165 | font-weight: 500;
166 | }
167 | @keyframes scaleIn {
168 | from {
169 | transform: scale(1, 0);
170 | }
171 | to {
172 | transform: scale(1, 1);
173 | }
174 | }
175 | /* Rename and delete btns */
176 | .action-btns {
177 | float: right;
178 | margin-top: -3px;
179 | }
180 | .file-list ul ul ul .action-btns {
181 | margin-right: 3px;
182 | }
183 | .edit-btn,
184 | .delete-btn {
185 | background: url() center / 20px no-repeat;
186 | padding: 15px;
187 | float: right;
188 | border-radius: 3px;
189 | cursor: pointer;
190 | }
191 | .action-btns div:active {
192 | background-color: #8c99b3;
193 | }
194 | .edit-btn {
195 | background-image: url(img/edit.svg);
196 | }
197 | .edit-btn:active {
198 | background-image: url(img/edit_w.svg);
199 | }
200 | .delete-btn {
201 | background-image: url(img/delete.svg);
202 | margin-left: 3px;
203 | }
204 | .delete-btn:active {
205 | background-image: url(img/delete_w.svg);
206 | }
207 |
--------------------------------------------------------------------------------
/src/FilePane.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import Dropzone, { DropEvent } from "react-dropzone";
3 | import { inverse } from "scpl";
4 |
5 | import { CreateEditShortcut } from "./CreateEditShortcut";
6 |
7 | import "./FilePane.css";
8 |
9 | type FileData = { type: "file"; name: string };
10 | type FolderData = {
11 | type: "folder";
12 | name: string;
13 | files: (FileData | FolderData)[];
14 | };
15 |
16 | class ActionButtons extends Component<{}> {
17 | render() {
18 | return (
19 |
20 |
{
23 | e.stopPropagation();
24 | }}
25 | />
26 |
{
29 | e.stopPropagation();
30 | }}
31 | />
32 |
33 | );
34 | }
35 | }
36 |
37 | class FileComponent extends Component<{
38 | data: FileData;
39 | }> {
40 | render() {
41 | return (
42 |
{
45 | e.stopPropagation();
46 | }}
47 | title={this.props.data.name}
48 | >
49 |
50 |
51 |
52 | {this.props.data.name}
53 |
54 |
55 |
56 |
57 | );
58 | }
59 | }
60 |
61 | class FolderComponent extends Component<
62 | {
63 | data: FolderData;
64 | searchTerm: string;
65 | },
66 | {
67 | expanded: boolean;
68 | }
69 | > {
70 | constructor(
71 | props: Readonly<{
72 | data: FolderData;
73 | searchTerm: string;
74 | }>
75 | ) {
76 | super(props);
77 | this.state = { expanded: false };
78 | }
79 | render() {
80 | return (
81 |
{
86 | this.setState({ expanded: !this.state.expanded });
87 | e.stopPropagation();
88 | }}
89 | title={this.props.data.name}
90 | >
91 |
92 |
{this.props.data.name}
93 |
94 |
95 |
99 |
100 | );
101 | }
102 | }
103 |
104 | // sort:
105 | // a.order - b.order != 0 ? a.order - b.order
106 | // : a.name.localeCompare(b.name, undefined, {numeric: true})
107 |
108 | class FileList extends Component<{
109 | files: (FileData | FolderData)[];
110 | searchTerm: string;
111 | }> {
112 | render() {
113 | return (
114 |
115 | {this.props.files.map(file => {
116 | if (file.type === "file") {
117 | if (
118 | file.name
119 | .toLowerCase()
120 | .indexOf(this.props.searchTerm.toLowerCase()) >
121 | -1
122 | ) {
123 | return (
124 |
125 | );
126 | }
127 | return null;
128 | }
129 | return (
130 |
135 | );
136 | })}
137 |
138 | );
139 | }
140 | }
141 |
142 | export class FilePane extends Component<
143 | {
144 | onActiveFileChanged: (fileContents: string) => void;
145 | files: (FileData | FolderData)[];
146 | },
147 | {
148 | showFileModal: boolean;
149 | searchTerm: string;
150 | }
151 | > {
152 | constructor(
153 | props: Readonly<{
154 | onActiveFileChanged: (fileContents: string) => void;
155 | files: (FileData | FolderData)[];
156 | }>
157 | ) {
158 | super(props);
159 | this.state = {
160 | showFileModal: false,
161 | searchTerm: ""
162 | };
163 | }
164 | onDrop(acceptedFiles: File[], _rejectedFiles: File[], _event: DropEvent) {
165 | const reader = new FileReader();
166 |
167 | reader.onabort = () => alert("file reading was aborted");
168 | reader.onerror = () => alert("file reading has failed");
169 | reader.onload = () => {
170 | // Do whatever you want with the file contents
171 | const binaryStr = new Buffer(reader.result as ArrayBuffer);
172 | let inverted: string;
173 | try {
174 | inverted = inverse(binaryStr);
175 | } catch (e) {
176 | alert(
177 | `An unhandled error occured while trying to convert shortcut -> scpl. The error is: ${e.toString()}`
178 | );
179 | throw new Error(e);
180 | }
181 | this.props.onActiveFileChanged(inverted);
182 | };
183 |
184 | acceptedFiles.forEach(file => reader.readAsArrayBuffer(file));
185 | }
186 | render() {
187 | return (
188 |
189 |
190 | {({ getRootProps, getInputProps }) => (
191 |
192 |
193 |
194 |
199 | Upload Shortcut
200 |
201 |
202 |
203 |
204 |
205 |
206 | )}
207 |
208 |
209 | {this.state.showFileModal ? (
210 |
this.setState({ showFileModal: false })}
212 | onResult={(name, color, glyph) => {
213 | this.setState({ showFileModal: false });
214 | }}
215 | />
216 | ) : null}
217 |
218 | );
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/Key.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type KeyNames = "save" | "closePanel" | "export";
4 |
5 | export const keys: {
6 | [key in KeyNames]: (e: React.KeyboardEvent) => boolean
7 | } = {
8 | save: (e: React.KeyboardEvent) => {
9 | if (
10 | e.nativeEvent.code === "KeyS" &&
11 | (e.ctrlKey || e.metaKey) &&
12 | !e.shiftKey &&
13 | !e.altKey
14 | ) {
15 | e.preventDefault();
16 | e.stopPropagation();
17 | return true;
18 | }
19 | return false;
20 | },
21 | closePanel: (e: React.KeyboardEvent) => {
22 | if (
23 | e.nativeEvent.code === "Escape" &&
24 | !(e.ctrlKey || e.metaKey) &&
25 | !e.shiftKey &&
26 | !e.altKey
27 | ) {
28 | e.preventDefault();
29 | e.stopPropagation();
30 | return true;
31 | }
32 | return false;
33 | },
34 | export: (e: React.KeyboardEvent) => {
35 | if (
36 | e.nativeEvent.code === "KeyS" &&
37 | (e.ctrlKey || e.metaKey) &&
38 | e.shiftKey &&
39 | !e.altKey
40 | ) {
41 | e.preventDefault();
42 | e.stopPropagation();
43 | return true;
44 | }
45 | return false;
46 | }
47 | };
48 |
--------------------------------------------------------------------------------
/src/Modal.tsx:
--------------------------------------------------------------------------------
1 | export const _ = class {};
2 |
3 | /*
4 |
5 |
9 | Cancel
10 |
11 |
Import from iCloud
12 |
iCloud URL
13 |
19 |
20 | Import Shortcut
21 |
22 |
23 |
24 |
25 |
29 | Cancel
30 |
31 |
Create New Folder
32 |
New Folder Name
33 |
39 |
40 | Create Folder
41 |
42 |
43 |
44 |
45 |
49 | Cancel
50 |
51 |
Rename File
52 |
58 |
59 | Change Name
60 |
61 |
62 |
63 |
64 |
68 | Cancel
69 |
70 |
Rename Folder
71 |
77 |
78 | Change Name
79 |
80 |
81 |
82 |
83 | */
84 |
--------------------------------------------------------------------------------
/src/ModalContainer.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { Portal } from "react-portal";
3 |
4 | import { keys } from "./Key";
5 |
6 | export class ModalContainer extends Component<{ onCancel: () => void }> {
7 | render() {
8 | return (
9 |
10 | this.props.onCancel()}
16 | onKeyDown={e => {
17 | if (keys.closePanel(e)) {
18 | this.props.onCancel();
19 | }
20 | }}
21 | >
22 |
e.stopPropagation()}>
23 | {this.props.children}
24 |
25 |
26 |
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/SearchActions.css:
--------------------------------------------------------------------------------
1 | /* Search actions */
2 | .search-container {
3 | padding-top: 5px;
4 | }
5 | .search-container input {
6 | width: 75%;
7 | }
8 | .close-search-btn {
9 | padding: 5px;
10 | font-size: 15px;
11 | text-align: center;
12 | display: inline-block;
13 | margin-left: 10px;
14 | color: #007aff;
15 | cursor: pointer;
16 | }
17 | .close-mobile-search-btn {
18 | float: right;
19 | position: sticky;
20 | top: 0;
21 | padding: 10px 20px;
22 | font-size: 30px;
23 | text-align: center;
24 | display: inline-block;
25 | background-color: #fafafa;
26 | color: #007aff;
27 | cursor: pointer;
28 | display: none;
29 | border-bottom-left-radius: 10px;
30 | border-left: 0.5px solid #c7c7cc50;
31 | border-bottom: 0.5px solid #c7c7cc50;
32 | }
33 | .close-search-btn a {
34 | text-decoration: none !important;
35 | }
36 | .search-action-results {
37 | position: absolute;
38 | opacity: 0;
39 | pointer-events: none;
40 | cursor: pointer;
41 | z-index: 2000;
42 | margin: 0 auto;
43 | top: 50px;
44 | right: 0;
45 | left: 0;
46 | width: 500px;
47 | max-height: 500px;
48 | overflow-y: scroll;
49 | background-color: #fff;
50 | border-radius: 8px;
51 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
52 | transition: 0.3s opacity;
53 | }
54 | .search-action-results.search-action-results-visible {
55 | opacity: 1;
56 | pointer-events: all;
57 | }
58 | .action-item {
59 | padding: 15px 20px;
60 | padding-top: 18px;
61 | transition: 0.3s background-color;
62 | }
63 | .action-item-unknown .action-item-title:before {
64 | padding: 10px;
65 | width: auto;
66 | height: auto;
67 | background: url(img/shortcuts_grey.png) center / 30px no-repeat;
68 | }
69 | .action-item-title:before {
70 | content: ' ';
71 | padding: 3px 11px;
72 | margin-right: 8px;
73 | background: center / contain no-repeat;
74 | }
75 | .action-item:hover {
76 | background-color: #fafafa;
77 | }
78 | .action-item:active {
79 | background-color: #007aff20;
80 | }
81 | .search-action-results .action-item:not(:last-child) {
82 | border-bottom: 0.5px solid #eee;
83 | }
84 | .action-item-title {
85 | font-weight: bold;
86 | display: inline-block;
87 | }
88 | .action-item-code {
89 | display: inline-block;
90 | margin: 0 10px;
91 | background-color: rgba(150, 150, 150, 0.2);
92 | border: 1px solid #ddd;
93 | color: #444;
94 | padding: 3px 8px;
95 | border-radius: 3px;
96 | font-size: 13px;
97 | font-family: monospace;
98 | font-weight: normal;
99 | }
100 | .action-item-description {
101 | font-size: 14px;
102 | color: #555;
103 | margin: 8px 0;
104 | }
105 | .action-item-url {
106 | font-size: 14px;
107 | }
108 | .action-item-usage {
109 | margin: 10px 0;
110 | padding: 10px;
111 | background: rgba(235, 235, 235, 0.67);
112 | border: 1px solid #ddd;
113 | border-radius: 5px;
114 | color: #1000a2;
115 | box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.15);
116 | font-family: monospace;
117 | }
118 | /* Icons */
119 | .action-item-photos div:before {
120 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAAAXNSR0IArs4c6QAAEL1JREFUaAXVW2tsXMd1/u4+yV3ucvmm+JQsUg/LlKxItlzHQdVYdlI/CrVA0wcCNE2DAIEDBC2Koj+KNg36q2nR9kfTokDTJika1GiAtLVr2K4cR3Viy66s6GFJFinLlMQlTUp875Lc3Xtvv2/u7obiLveuWtZoRpqde2fOzJxvzplzzp17acEnTUxM9JPkMeZHmIcty9rmum4ryyaWYZ/uW9rMOfOcc5l5jtdpDj7K/Brzy729vTdqTWZt1njz5s2HONiX2f5YLBYLRCIRhEIhBAIBk9kG5Q8zESBK2bZtFAoF5HI5ZLNZh3y8zLYv9/X1vVGNpwpOR0dHo/F4/C/Y6fOpVMoiyGr9/t/VESzm5+ddLv7fZDKZLw0PD6+tZ/IOoJRiGwm/S6CPNDc3r6f7ibleWFgAgb5GQR2ndG+XGC8DTafTEt0rTU1NRxKJRKl9S0r31iXYl78LZ+IU3MwHZkwr3oVA7xEE9xyH1b53S+YpDbK0tITl5eVTvP94T09PVvXrgX6NkvxCMpks0f/vy8IqCj/8Y9hXnqs5VnDXUwg9/DtAqKEm3d00Li4uCuzXaKSeUT8D9MaNGw/SyLxO9IG7GawmLUHmXnwG7sz5mmSlRqtjBJFP/OWWgqWWOkwP9/f3nzLACPIrNDxbB5LcF07/GTB7DlbQrSuL1vQpId+CUpiY/lBDWUU/+f5WStNdfB/5538ZcO27Y9cKIvzUP8NK9N1dvxrUlKrL5sEQrdOTNEB3JU372lXkXz8J572LNC5zVDcXgbZWBHePIPTgMbiTL8IKFGpMv1lTAc71F2DtPobC8j/CWXsVVuEmAlaBvrsDgehPwYr/ChA5vNkAFfW0Oxb36pOS6N9RxJ+px1+69FWr3/oWChdOAwGqZIh+WjngUD1ZBilBxkpu4BqiO+nGgmVbV8FA1QrXwWosCtyfgeXmEQDnsBxOxczSAkvVNX4CVupPOK+/C5R/pcv5RogT3qOIxy8JZParfwpnaoLgalA7DtZO/gj2xRBix4e4APWBZcyDzGIa+fwqIk4fgQUISyBZaggqYIClqVt5EbCvwWr7ji9YYaPW7pLK9geDwRqce02rX/8H+kGFl/5Jq2+ns1h5bozE5JAGqVZ2qR2LmQmsFZZI7khmcFzLK3ntGrAEzJKbhGBZl7sCd/63fZkpYusW0JRfzGqPvofC22d9By0TaN04coFgs88p7hZQFlWyQM4LZH4ZDttdaotbAmmASWE3ASvJ5v6rPG21iyI241LifkBzr/6g2hib1jHuR4BMKzuTK1h9fpR7zTZ7Wvu6lBGyMZclSHvZANRCEFsRWFGiVcBK4mXJZr69KR9qoHtRkdBvxA9o4eIVEdefLLKxDqw7uYq1568aw2LRQiuDxuv20hRW7Iy35yVJ5SJQT4rVwdpckTLYtdfr4Stk4PpRuvMLfiR3tlMyUl0uvFfy2ibYVYKVb3UDNqYJMlOgupLGpvpKhU2WVI3q1pZsCazjzNw59yZ3dQE14tlkgGrVRm0pHVNyhpIaY3IN+e+NY3p5BsuFDFwtiFFX7sPyNYFyhTzDUw9YdfRPdQFVMHBXSaNqfoI1pa4pMVne6StZpEfnDTCBc4oq69AN2UV6PUVvBrZsqEoGKqgDEP9UF9DQ/nv9R1pHEZA0CcxkRU0EaOoIbGyFYM/lzV7UfpR6m5LXfGrmAmhfSpLVwZqQoWSV2TkQPUpK/1QX0MijHxPn/qMVKdwQEYl8fZazZ/UMg6cst5X2psN2m4ugvemoZHbCQc/qsrsfWFo1hJp+tS6+pCw1U24ug4WrtzHrNGLhxL/Bcgq0nlQshn0KDAIsgyqlbGQ8FLVQIKJoxELXEOs0A0EpOxRVgaUkaNMoWWE5fyXJEHhv3sEUJb5ybRa2Y1PzXZPD7BPiHCEuiAItDRnhojU3P4iexovoampELNJtxtjsZ1OgTt7G5PcuYv4M3YKdhzV4AG78NRSmxw1QA1JuhNkhEzL4inkLGYZyeRfj0w4azwEH77fQPsLpi0CN0SFQh2pKDTRpbNHBv47ZyK45aBkGGhfXEOa4YY4nBs01acMEagCzLtbYibwbx/wH38To9DcxkDqK4a7PciEYK1dJnLIyCeTVZ9/C7JnrihMNgcUwMfULn0eky/8RqikcAjWQPhL44WkXk2+TS2lzWK7EU1n5TBmis3MFfP1CHnMEiWgAoUSc83krIMPjpeI9b1QXauhGNMUnGKmQ6mBjYv4lnL35e9QaPkxUSVWB3jjxLjIT85XkDJCbjwtsb2Xbuhqdgva3hI1CaoKzFxxkGCaHCFRg3Uby2Gjhg5yLZ0dts3flahoHU6aP1lZWV4BN4MArLbfUPBjdhobUA2wqLQIri2lx5TLGpnlKUSVVAM1OLWH2wuQG0nWDBgn26c8h0lkbbKKxATtaiUpdyeXlk/yh6vVSYIntkqqLfxnLYY0bV9a2aSCFEI9WBdCLijxwAuhlSXIb4i2HiXEdPxs4nV58GZm1dzfUcuqNNbfO1/GEIsk+/RuIdvZs7H7Hfaopir2dESQjtLaM9KR2B7ss9HwsgjwleIlKE2FjYqgboWbvUE5Ay+BI790TZLQbTc2SJDv6pOnFFyooKoAujs9WEJUqxEA50TEmn/h1RDq2latKF3nqmHKBHaKREHZ0N+JAXwOs/oeQ/Ow/YefDz6LQeQSpPYOID/TQF0aRowHLcnutUp1lnUuRkVCHqa6JlgcpSX+Q4mEhy4OBDanC6uaXqm/mDf28W4H95K8h+8q3cf39MVxay2Gu4O0quQax1UAtG+Ap5r2UXOjY7/Ic9+fQxvpBtwEruS/i9Jl53Lq5BkerIvnR2krFWztCOLK/AQeHdxDk/ZSk1+5NXPt3rVAZ/1ZItPYQla0uV3lqx6OY6D6MTDBRNh4mWCe5Tcu4FGrDZMsjWHDlZ7x0bfkIrk0d43uTTj5rF9ngogiONvZaLoax23txeW43pbv5nvRG8/+tkGg4EUV+3hxu+/aW67k8NoWF2WUk2ofQ3LaDPpcHWfYKXar3fBG2wgxx6WeYx164hFwwgqupJP7+/ByDjRg6dz1GKTN4cFYIL8+ggL4zpBdY9M08YBufWYFDP350OOHFzb5ccbuEOiqoKoAmB1txuw6gLs+G3hmb5IudrHHqJSNimVCR+5EMW6SZy2VxamoG6ewSBt4cQ+7NKzjT0Q+Hoc17F15GrLsZXfuHEE1GTdSjQIRHf0ZVaY+NDU7Pr+A/GVAc3ZU0NBUoNlQ0xw5tqKliddtHaltSjeDQWpwfncStOa627st5ncVk3djyEr5z7QbGV5YJpJehXwDnb6SxcPECZ6ZLSQ5idWYW1199E8vpKfag4gooS5okY3+1Z/Vvggv6/bFZqrGn3CTaNHUmf7airWKPxroTaL2v0pKWejo8vDpHkDOcuAyQW8iwZ0oP7I3sMk6kJ7EmRhnwJnc/TN8ZQHpxAWszadY5GOh7goBpsij5qdOXsELQJaNjRjEgvZkFzwN7uybYzuRjiEd3l9gtlxVA1dL/6G7Ee1NlotKF9uTZKwQ5mymDFAMGsEDyRtcFWqKT6RkGZh7ojj1HEUy0cAc6dDs2bMXOTh4N0Xb0DzxFcOzEsdNnebKn/Uw6Y329kY2UTRhB4ONc4FfGblUFm2zcg6HOZ9i/MlUFGmCguvNTD6D14AAnFBcC4eLMu2l8UARJbdJ/w4q0yVyTVOXV5UUsCEw0hq6RJxDdttME8TqSFigZUZuaIem1p/Zj5z2/hFCkCTYd6eoMjzyNJDUSM69LpQ5CdX19IYsTBqyWVUMG0Zt6nL76j7gjqgf1FcbI9OSPwPY+PoL2Q9ux+M51vPXij3B7jcaBoZ0lJrnyQZaWy8c2SYCSEhNWOIx5PtJ1pXYh1rHdxN16OUE7atTVFePiX06WBkt7sSk5hHsTX8RSdgwNfAvXHA9xIXhaT50I8PQwwDMmXeskUYZKBmuG7n50rg+/uO8oH9MO/c8f08iGSdG2BKIj92D+7Tl0tFPCrJUaqLQoSvEroauOft5cu+fG0UDpGPcnIQgZU5Cho1FJ0YdZZwbyjA3PCpBI7ERjQwRD/TRcWjzo2ZdBP/9RBzieFlVgvfsJLnS84RGC9H81oal80+Wzk2b/lfacphHrCsaLO8qwoiBBaqxA3WabWNVzp0PxKXCwWTbHkwinmnmawEd1zm4kLClzlVxa4kK+UBdIA5zvZ06m3/HlXwR1AZ14f84AETiBFdD1YLV/DSjVE2xBwTiBeSB17S2Kyu09/Ujs3eUBk2QEkqXkqlG9kTw13UySHhfeglyaG2c//7TpHl3fdXFx1dwKnFZGoMi6YU2qK301/o2l6j2AAUOle4OFTSoH7x1GuC1Cg8LXjRpDlSapZBYdVbQekAzDcDtX5bm5OOL6oi6gdpGXkvhLYHVOJNXdCNYmszJAopMREkL+oqmrGW0j28GvsbBGtZtaFpOefhhJUrIO1fdOkJK6aDST9MmTpEBqz5ZOQNhYM5V4r0nUEI8W2SmxZdaedZ6f9PYp64qS1Z40+5f32qtS48buFLo+MszXDlxbHsscHNiBTn79osBB2ayErmmJBcgzPKr3AJZBGtpSu43mSH0feAhozm9VegZoPMiJWFifJegS2FK9AUsJlg6/tGcbCHKbAckFoMSUdQb1kcFhdCYZmMhc04Xo1EFj1gRZXgQtQAHDKf8zLBEKaMYP6J77uo2RKYMl8yVgXulJtlwnKysVZHgX627FtkPDPAjjea2pU72XjWQH96KzuYXgCdFIq2jWqklyA0i+hcVHu3/86EcsFYlfpahuSUDn/YB29yQwuLOtDFZ7VpLSEJKAyvWuR3vUKapr9+EhqqtA0gpXyQK7f+A+dCTaCJbjUbJ3qqvU1Juh7EcpSYE80L4T9yRrS7SIbV5Ab+oDQr/0M48PIdUWMwC1oTaCFeCS69GjWqy7hZL0QEq6tbLAHuC5cXuynZGVOPH2pby0W5Tsj0EqgLDRE2/Fp4ef9GObcbXBNiWgV/WVpF+KRkM4/qmRsmRLYDVMSbICq5hv8L4+bDssdWUox/t6ssPTxf0DhzDSN2RCvM1BFowkf3P/pxGr40szYWO8fkWf35zK5/OfEY9+SWA/+fQeTKWXcPnCFNLXF7CSYdBJVY0nG9C/vQV7DmxD9/QqXro4Z9R6szHNolQ0BvHz+x6idAM4OfkOLs2OGz8p9WuOxIzh0Z70U9f1wxIbnwKdN0ofVI3zgyqyuzXpdiaPr56YMOHg3YxIT4Tf+ngvOpuM/t5N101p9UEVJbo9UPxy+SV9j7NVqS0exgPbeVC2wcr63avPVoIsYnqJQryuPSrR/gE/6tVW27L09L5W9LU20IExlqkji1Z9tjIJk7BpTANUXz/y+q/1Ue9WpQjf733hoU4c7qdkaZBqZdGIVn22KhWx/FURmwm8zNjFT8y//3/xYfL1hRx+cDOLy7fXMKdXbEwtjUHsaYvio30xDDTzncUWJn2YzPwGJfrT+/bty2noO5aQG7edov53foH9wJZ+oLyFIPyG0gfJBPkWvy96gnvzVon+DqCqJNgYzfmf8/JzP4l/PEC+/5ZW9ksEeYd1rQAqsErFPwf5Ci+P8ctPq/TnIPq2TgdmxS+yDO2H+UONM49mG/4cRG75Pyig36/7z0E2Mr3xD3zYrhejfOSA3vOZR1GWH1bSBl9k1oPsBLM+NHyN2fcPfP4bJeMN5FYVPbcAAAAASUVORK5CYII=);
121 | }
122 | .action-item-gear div:before {
123 | background-image: url(data:image/png;base64,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);
124 | }
125 | .action-item-calendar div:before {
126 | background: url(img/var-sheet.png) 0px / cover no-repeat;
127 | background-position: -23px 0px !important;
128 | }
129 | .action-item-files div:before {
130 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAAAXNSR0IArs4c6QAABHRJREFUaAXtm99rFFcUx7/n7uxOsokoTVqNiRpR6INv0taqVdJiSvH3Uwv+AZZiGws+tKVK6Zt9KNgohhRUxEqKIvqgUBpEWhUUW4pCVUrAgiaNNbFKrWZjMrfnrM7uaLLZmdm72dmwA8u9e+ecc8/nnjszd2bOEPJsHU268XEKrdrBCk1YyOKzofECEZJa66o86kZ3E9GQ1ngIwl023EcaPaRwNm6j+4Nb1DtRZ5RrZ/tM/boexacMtVZDx3LJRaGdQKMMf5Ji2Nl2my6M59MY0PaF2nbu4Rserc0MOGb/eEai0sbAmj3+Vs3A1rYeSnn9egZkT6OuG03hBE/JN7xC5VbnKX4uZmPjh7006Pqu3MqRRTrBkMfLHVJ4hGFkCKc6Z+uky5cB7e/H1yywwt1R/qVekkrhK5cjPXV31+tXRjUu8lhkwF2B8i7JiRGWfDRAv6TBHGD71IOUEGn1hA0guU6mHuFPbrTKO3q5vKcRuxrNangI66YupMBrSxgtXhC8mmss/LTPXAws/QyoXwTUvPTM1WqM+t9XNLreGtNc/AZmVE+XdaE6E8j3fgDmvUl5Ib0dKD5I1ncB05u9rcWrC6OcjJrCdrHscz7I1cRRzGV7fith0xmg4bVcEkbbmxQv9aaHNSnTtZAtMY2w8QjfJSwtxEp+XWFUvBiuzi86vkSyPlw0vdYStQz7PdC43NtquE6wFa+GMsskw+Z9m4vXEDbwMTtnpW+VQILMWBuZlVA8SVh/GJjbEojBt3BkQMVjq5qw7js+ixfhEhQp0DRsFWHtIaC51XewfAlGDjQNazPsQWD+O74YfAlFElQ8jyUIaw4AC1b74sgrFFnQNGycsHq/mUtPSUCdEWDgms4bBRFQFqFlpy/RCYUm9dYs+WLWl8P8LENZ/mCzWuFrkxrR2gbC4i1ZZyWyfn9ZrXA12lXnhB7WrQPhloAP/uKn0HeCO1zILd6kTl0XTSJb2+D+C1KGjgkmdeoGQTItWwE1PaKltleJaKkjYLr/SkRNj2ip7VUiWuoImO6/ElHTI1pqe5WIljoCpvuvRNT0iJbaXkERHRkKf38YFLzQvhTn5DwM2qkrf++GWyt++U9P+D6Y8YHiN96Pwpr441hYzeB6148G18loaKTkjff9TEPAyq97gKtdnEjHmYjF2hzOC7q8T+O3jvA9CCPtqtc/sacFvbCbNgeoexnwPs4M79YTTRm7/24Dg9e47C/QGtHPlqSCss2CQP+9CcgvqpswyhvvS1F10JhfzKjIxklOuXCMGY2cIXKsBE6ptl66xb51R84/cw51b+mjm+6CYcfUjGo66XG7jFka9ONBusSVveYGMRqWhEkyOzOgUpk1C9tAdD4aLprwgi7aNj5xLblTF+/+TsOWjQ2Spu3uLNdSGKwqrHm/L7u8zYAKlOSg0wys4sh2SiJ+uYGmfRbfmcGbTy8cOd/7Pf0c5AteobwtCb7RhiaHv8P5kT8H+dL35yDPA7U36iYMY1WUP/CJxXFaLiHP++79/z+rqj3sk2nOmQAAAABJRU5ErkJggg==);
131 | }
132 | .action-item-music div:before {
133 | background-image: url(data:image/png;base64,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);
134 | }
135 | .action-item-sharing div:before {
136 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAAAXNSR0IArs4c6QAAA8FJREFUaAXtm8tPE0Ecx38zfdCHD2w9CPSgRhP/AA0a5YaPBEy8ecdgNBg8GKMHjXrTgweJUZGL8aDxJAe4QNQDkkjwakKUBBNK4SAFjJQutTP+Ztit26UrpXRrd7tzmcfOzO/3me/s7Gx3SmCDEHz6oyniCZ/McmghAAcogUbgEMFmISAQ2KB5eS9zSGOHKbSbZBwSHGDSQ2AkmV0eXrm8e+ZfxtD3wiHSlzpax+EmAGmnAJ7CtaqjlAFkAfiAQuB+sjP0qZBX60F7vtU1BWKPsPFFBFx/vVAvVVKGPnP0+flMOn4Vug8qerfyQJoe/4wyn68fp+cJfSW7pXFaf6SZzLmZKzvmNd9xANRw94uf+X1v7Q4paCSD3zfY2JsIaXg50D0N+x5ipkW74IC4mUH9A41DTt2GZ8uHgdAxHIkcuFah3PGxBgoLCoeJJK6ZFgecwgw4a569FP68BkbJrUpAHm+k8PKMH960+eFQJG95sARZMiGb6JyI52Q9DX/HQq8l1tROBeSL034Iqlbm0xzOD65ariyq+nuRLe+lERI8W2lIwR4NkIooK9gkY5bQI5VS8vUEPtfV8GGa/YXdZe00FowIDAc04+WO9dNVQF4fyeRMdAytwnsNth3vWQthBSPF1SiWs25R4pUBUpjJ4DbmggorzVooqmD0Mgo7qUUr/WiCQVu/Al8XChvQYGPbCUwtFa5TjrGXjPikCZajM7M+zCC1+gLWSkhph0EdxZs0t03SjDstRsZtlu+EqmXQStokxDuLf9+O9Yl35Y2DFX3qrbqK6kfDLF2sWmbt9eXF9LUZ1fV9i3TNKOqCGqW3e95V1O4KGv13FTWOiN3zrqJ2V9Dov6uocUTsnq8ZRUt6TduKusVs3rfSv1nbmlHUBTWbAnYtdxW1q3JmfruKmo2MXctdRe2qnJnftaMofvpOmY2CU8qR8RfFX3ZXnAJkykFBoZTBkmkFh1wQjF78PBnHG3V/KUxb+URQir1S20hG/M48WWoHdmknGL0ezsbx1FjHZpz+X++Um/FRX1cw0uRqekAeJdNfcVBasPFsepCudEfjeKJ62EFs+SjIluiKTssNA8+y205UdY2JybOAEnSuKzzOgT/JHwr75wSTONkpSCSoSMzNTl3DZXhUpB0Sxigs3tBY8s5r1cZRc0QXZ9BnlXgrJntRXeuOcmnDXOZY9blXMOjP0wszeYrq7cq/gzByB8tO4YGk3BTX16mWtLqQDimU3yv+7yAG74M987FIINRazX/w4ZnUO/EIMbiel/0D9F46ra4okMoAAAAASUVORK5CYII=);
137 | }
138 | .action-item-text div:before {
139 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAAAXNSR0IArs4c6QAAA+NJREFUaAXtm8lPFEEUxr+CYdFBUVBxTTTiwRjFBQIHvRj04hpv6t0rBzV60Cg3jPFgTIj6BxiPHrgoehKNRDRyMEZF0eCCYUkgYECHab/X4wyzEaeaGma65CWV7q6pevV+9aq6urrfKPxDnFdYgynsY7E9TNVMq+GggseFTKVMcykTbOwnFIZ5/MbUw/QYhWhX2/GV5zOKmukX5wUaCHSevx9kKpypXJ7kT9GONnZAi9qFZ+lsSgF13qMEo7jOwqcImvJ7OiV5k6doMXAbi9GkNmEy3q4EEOcNKjGOeyywO76QD887EMRRtRlDUdtjoM5rFGMCD9knMhdtkE5C7FW1nNOUghjRBK5ZBClY9Zx4V6J8rkedl6glZCfTNHi0hJ+PCmHC1qud6IqAhXHBOkhxkDhO2CjKXSdD+MTzgGRYKCGSrRfiQ4SzFVL8FhDGArq3zkIvJiKRUeaoPNbZLtXi0bW2UwqjeLTcelAyCugC60EVSuRuK9utzKXqDLDqMvczwczrmCgZ5g7tezPQ36KvzUGZcro4gnVkWz9QVKVTw1zZELeh3ZWe9EWejHSqDt/VKW227Cza1veomB5Yzu1ADoZuiKPJo3h7IgoNsDlJ/hH9oesftgRL50ETusOCC/05Wsg3nUuO8GZUZg5/8h0wet+cvjSa9EE38t3Zoiy8Vuo9CQzfSWOimSz9OVq8zkzLyVqKsqT3bzv6Hu09DqxoMj90B24koxu99vbAYNSEuVGmP3Tnxi7jrcyDGu/SHCv8bzyqf9etOAFUnc3B7mUc+HHV81qrf9et4a4lsCw3AzE0yI03t4geRH/ojj310IyhKrNoW9+jqohvmer4zsjgs24m/TA1xo/6z/k95XcmpVPK6IOmqPBHhv7Q9QdXipXzoCld4vMM/XVUbkbBesO7l15g8m1Wu1IfdAPf6y49ZtYoh+/QPxwGRtrM6o3Tpj9Hgw1x1Q2dKoZSyJKVRRGPSnhK5t9f+rjpXsmAMpPr6ORHYPBm9jAVxmQd5XMVA6nsliEZuiN2M7p0IwWMw/liPSgZxaMSCmq79IhH+aRsuZBROd0M1gjhMz8H6y81fugfCZMrZkCVqnHnaLsfbPZoY7vair6IFwtwkUM47FFR/laLBD26sYAuqNrhztPW/LXYs2WtEtkptafnZSlO8/qJZ5X5V1HCcs9FzYqBqi34xfBsfg9ER/RHHx8l1PxANPpaOGKgcuHGoJejkfP1FpNeWI4oyLWIzWI7GeLj6cWsWEx9so3u30GAS8zfn/dLT+RG+oC2Nmf8d5AU4Mg628h8+fpbzZR/f/ApxiNZQpJtj7/+Aycd2EjTXPDBAAAAAElFTkSuQmCC);
140 | }
141 | .action-item-web div:before {
142 | background-image: url(data:image/png;base64,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);
143 | }
144 | .action-item-contacts div:before {
145 | background-image: url(data:image/png;base64,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);
146 | }
147 | .action-item-location div:before {
148 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADoAAAA6CAYAAADhu0ooAAAAAXNSR0IArs4c6QAABJhJREFUaAXdm1+IFVUcx79z3VaxVtfU0lTUhYzEIlvRTaVEEwtN88mCeoggpVBDeomlvAkhpaAhlVQPooLSUxAaqaiQq7v4sOqDhVZqpZZ/SLr90W3d6fu9d7c7d3bn7ty5c+7MnR+cO+fM+c2Z32d+M+f85sy5FvqTZnscbMyHhdnc3k/10Ux3M93FdAdTJeVfnuxPpt+ZLtGms7TpCLf78a71czFDLM/KZruJjaRZP58p5akXj4oumrGfwGkCt/ZlUm/QlfZA1OEDKr/C1Lu+r1bis8+mKZ8gg9XYYt1ymlUI8qY9nGhfUGG2U6kK80d4Nz6L9db1HtvzoGl7MDpwkBUzeiqrfNuGWsxF2vpbHPlnrwMbWU4KpNhm0HEblJHkPNpsT6erj7GcB89WV/1PF7lm8hZuy4HZWJdASHkpRVe+o4yF3Dh5PrtTe5InNinHy6MLmZJ2yzrdpcdzYYr3cJI6ICegM98kTzY49yQyb2OSQMclEq4QapRA6wv3JbJUL9A7E4lWCFUn0NrCffEqjR3K98HyLaypiRdWzppBtOrFqcCqx4Ap9wLXGa02fghcuBHc2liBjq4DXmsClk8HRgzOQw1nvnFMAkAFsZreW/Ywn6MBecCeXMdtoOVCTynYNjKPDmDvsORBAs4EHp9Q3Pjdp4DfNIFShlQcdMhA4OVpwEp6cOIwf5ZvPupPr5hWxUAbOJ2mzuWlRkCwfuXwOaD9kl9tbz3joE9MBF7n7bmYt2lK4XWJsuGbEg/wUDcCqg7lOXYsev4evc/jzD52n74CfHXGh6IPlVBBRzLGWsGh4VW+D43iUFGu6Nm0Na8XgoQC+hAH9VX03guPABrsw5BrDBJ2ngijpVwbgc3S8/b0JD5/s4B5DZyqCPD8FcPY2gb8o3n5kCQQ6NLJwPoFwAMjQrLC1czNTuAjgoYpJYMOHQR8/jxQo9cBQ7KjHbicCbfxks3N3AK+Pgvc1tcOA6LOJ4wAwW1ayR7toiGLtrub8VeewEjo3BvFdfd9D2hYCVtK9mg5BpzXx75+ZFNLPwoBqysK2p+N8qQ8akJiBfo+w72wAgT3xYoNqHrZXSfd5oVXjg2oxk29YJuSWIAqAlIkZFJiAaqYVrGtSYkcVOOyqSHFeeEiB/3yO+Dbq06TzOQjB91sKEBwX65IQTUXpDmhSkikoCaCd6+LJtAOr0qT+xUgaL62QtIp0L8qdLLsaU5czp3trQNmAwQXU0avafp043Mq2XV4gOJT24DJ9wCHfgxwcPBDbgj0FybOvlZG9Gmh3M8LASz9VbfuDwEOrK5DLJzRgiPDUWYsrkmrPLqHiYFYYsVGJ/amulcu70ssJict8J71kzwqf67lr6F5vewZovrRokexdS+N4+pH5rdGZY3B836slZ1qP+dR5TJYw98kdUytXG8jpqzkQbUGvRaLuPd4d101b46T5Rmuvv4/vM2DCittXaPCHA45n7JUjT2xbP4syyAWh3h/A8v9HWQddZ9k8tZzNBZhVoAHaOXb/v8O4ra29x98xlClnmkIUx+LZdwNhFrWPOEfTIrPLxLM9x98/gMUcPpQ6NVISQAAAABJRU5ErkJggg==);
149 | }
150 |
--------------------------------------------------------------------------------
/src/SearchActions.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import ReactMarkdown from "react-markdown";
3 |
4 | import { allActions, getActionFromID } from "scpl";
5 |
6 | import { keys } from "./Key";
7 | import { categories } from "./data/ActionCategories";
8 |
9 | import "./SearchActions.css";
10 |
11 | const actionsByName = allActions.sort((a, b) => {
12 | if (a.name > b.name) {
13 | return 1;
14 | }
15 | if (a.name < b.name) {
16 | return -1;
17 | }
18 | return 0;
19 | });
20 |
21 | class ActionData extends Component<{
22 | onSelect: (text: string) => void;
23 | actionID: string;
24 | canShowDetail: boolean;
25 | }> {
26 | render() {
27 | const action = getActionFromID(this.props.actionID);
28 | if (!action) {
29 | return null;
30 | }
31 | const usage = action
32 | .genDocsUsage()
33 | .replace(/^```\s+(.+?)\s+```$/, "$1");
34 | return (
35 |
{
42 | e.stopPropagation();
43 | this.props.onSelect(usage);
44 | }}
45 | >
46 |
47 | {action.name}
48 |
{action.shortName}
49 |
50 |
51 | {this.props.canShowDetail ? (
52 |
53 | ) : (
54 | (
55 | action._data.Description || {
56 | DescriptionSummary: "No description"
57 | }
58 | ).DescriptionSummary
59 | )}
60 |
61 |
{usage}
62 |
70 | See futher documentation
71 |
72 |
73 | );
74 | }
75 | }
76 |
77 | type SearchActionsProps = { insertText: (text: string) => void };
78 | export class SearchActions extends Component<
79 | SearchActionsProps,
80 | { searchTerm: string | undefined }
81 | > {
82 | input: HTMLInputElement | null;
83 | constructor(props: Readonly
) {
84 | super(props);
85 | this.state = { searchTerm: undefined };
86 | this.input = null;
87 | }
88 | searchChanged(value: string | undefined) {
89 | const searchTerm = value;
90 | this.setState({ searchTerm: searchTerm });
91 | }
92 | render() {
93 | return (
94 | e.stopPropagation()}>
95 |
(this.input = c)}
99 | onKeyDown={e => {
100 | if (keys.closePanel(e)) {
101 | if (this.input) {
102 | this.input.blur();
103 | }
104 | this.searchChanged(undefined);
105 | }
106 | }}
107 | onKeyUp={e => this.searchChanged(e.currentTarget.value)}
108 | onFocus={e => this.searchChanged(e.currentTarget.value)}
109 | onBlur={e =>
110 | setTimeout(() => this.searchChanged(undefined), 300)
111 | }
112 | />
113 | {this.state.searchTerm !== undefined ? (
114 |
this.searchChanged(undefined)}
117 | >
118 | Cancel
119 |
120 | ) : null}
121 |
128 |
this.searchChanged(undefined)}
131 | >
132 | ×
133 |
134 | {actionsByName
135 | .filter(
136 | action =>
137 | action.name
138 | .toLowerCase()
139 | .replace(/[^A-Za-z0-9]/g, "")
140 | .indexOf(
141 | (
142 | this.state.searchTerm || "kjhgvb"
143 | ).replace(/[^A-Za-z0-9]/g, "")
144 | ) > -1
145 | )
146 | .map((action, i, v) => (
147 |
{
150 | this.props.insertText(`\n${text}`);
151 | this.searchChanged(undefined);
152 | }}
153 | canShowDetail={v.length === 1}
154 | key={action.id}
155 | />
156 | ))}
157 |
158 |
159 | );
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/UploadShortcutModal.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/UploadShortcutModal.css
--------------------------------------------------------------------------------
/src/UploadShortcutModal.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { inverse } from "scpl";
3 |
4 | import { ModalContainer } from "./ModalContainer";
5 |
6 | import { DownloadStatusSwitcher, UploadStatus } from "./DownloadStatusSwitcher";
7 | import { downloadShortcut } from "./Uploader";
8 |
9 | import "./UploadShortcutModal.css";
10 |
11 | type UploadShortcutProps = {
12 | onCancel: () => void;
13 | onResult: (filecont: string) => void;
14 | };
15 |
16 | export class UploadShortcutModal extends Component<
17 | UploadShortcutProps,
18 | { uploadStatus: UploadStatus; shortcutID: string }
19 | > {
20 | constructor(props: Readonly) {
21 | super(props);
22 | this.state = {
23 | uploadStatus: { uploadStatus: "None" },
24 | shortcutID: ""
25 | };
26 | }
27 | async convertShortcut(data: ArrayBuffer) {
28 | const binaryStr = new Buffer(data);
29 | let inverted: string;
30 | try {
31 | inverted = inverse(binaryStr);
32 | } catch (e) {
33 | this.setState({
34 | uploadStatus: {
35 | uploadStatus: "Error",
36 | uploadError: `An unhandled error occured while trying to convert shortcut -> scpl. The error is: ${e.toString()}`
37 | }
38 | });
39 | throw new Error(e);
40 | }
41 | this.props.onResult(inverted);
42 | }
43 | async downloadShortcut() {
44 | if (!this.state.shortcutID) {
45 | this.setState({
46 | uploadStatus: {
47 | uploadStatus: "Error",
48 | uploadError: "No ID."
49 | }
50 | });
51 | return;
52 | }
53 | this.setState({
54 | uploadStatus: { uploadStatus: "Uploading" }
55 | });
56 | const uploadResult = await downloadShortcut(this.state.shortcutID);
57 | if (uploadResult.result === "error") {
58 | this.setState({
59 | uploadStatus: {
60 | uploadStatus: "Error",
61 | uploadError: uploadResult.message
62 | }
63 | });
64 | return;
65 | }
66 | // done
67 | await this.convertShortcut(uploadResult.shortcut);
68 | }
69 | render() {
70 | return (
71 | this.props.onCancel()}>
72 |
77 |
this.props.onCancel()}
81 | >
82 | Cancel
83 |
84 |
Import Shortcut
85 |
{
89 | const shortcutID = e.currentTarget.value.match(
90 | /[a-z0-9]{32}/
91 | );
92 | if (!shortcutID) {
93 | this.setState({ shortcutID: "" });
94 | return;
95 | }
96 | this.setState({ shortcutID: shortcutID[0] });
97 | }}
98 | />
99 |
{this.state.shortcutID}
100 |
this.downloadShortcut()}
103 | detailsMsg={
104 |
105 | To convert the imported shortcut, ScPL Editor will upload your shortcut
106 | to{" "}
107 |
112 | shortcutsweb.app
113 |
114 | .
115 |
116 | }
117 | uploadAction="Import"
118 | additionalButtonClasses="large-btn stretch-btn"
119 | />
120 |
121 |
122 |
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/Uploader.ts:
--------------------------------------------------------------------------------
1 | type UploadResult =
2 | | {
3 | success: true;
4 | key: string;
5 | link: string;
6 | expiry: string;
7 | }
8 | | { success: false; error: number; message: string };
9 |
10 | export function uploadShortcut(
11 | shortcut: Buffer,
12 | filename: string,
13 | onProgress?: (percent: number) => void
14 | ): Promise<
15 | { result: "success"; url: string } | { result: "error"; message: string }
16 | > {
17 | return new Promise(resolve => {
18 | const formdata = new FormData();
19 | formdata.append(
20 | "file",
21 | new Blob([shortcut], { type: "application/x-octet-stream" }),
22 | filename
23 | );
24 |
25 | const xhttp = new XMLHttpRequest();
26 | xhttp.open("POST", "https://file.io", true);
27 |
28 | xhttp.onprogress = e => {
29 | if (e.lengthComputable) {
30 | onProgress && onProgress(e.loaded / e.total);
31 | } else {
32 | onProgress && onProgress(0.1);
33 | }
34 | };
35 | xhttp.onerror = () => {
36 | resolve({
37 | result: "error",
38 | message:
39 | "An error occured. This may be because you do not have internet access or because the file.io servers were unreachable."
40 | });
41 | };
42 | xhttp.onreadystatechange = () => {
43 | if (xhttp.readyState === 4) {
44 | onProgress && onProgress(1);
45 | // Typical action to be performed when the document is ready:
46 | if (xhttp.response) {
47 | const result: UploadResult = JSON.parse(xhttp.response);
48 | if (!result.success) {
49 | resolve({
50 | result: "error",
51 | message: `The file could not be uploaded. Error: ${
52 | result.message
53 | }`
54 | });
55 | } else {
56 | resolve({ result: "success", url: result.link });
57 | }
58 | }
59 | }
60 | };
61 |
62 | xhttp.send(formdata);
63 | });
64 | }
65 |
66 | export function downloadShortcut(
67 | id: string,
68 | onProgress?: (percent: number) => void
69 | ): Promise<
70 | | { result: "success"; shortcut: ArrayBuffer }
71 | | { result: "error"; message: string }
72 | > {
73 | return new Promise(resolve => {
74 | const xhttp = new XMLHttpRequest();
75 | xhttp.open(
76 | "GET",
77 | `https://shortcutsweb.app/inspectshortcut?id=${encodeURIComponent(
78 | id
79 | )}`,
80 | true
81 | );
82 | xhttp.responseType = "arraybuffer";
83 |
84 | xhttp.onprogress = e => {
85 | if (e.lengthComputable) {
86 | onProgress && onProgress(e.loaded / e.total);
87 | } else {
88 | onProgress && onProgress(0.1);
89 | }
90 | };
91 | xhttp.onerror = () => {
92 | resolve({
93 | result: "error",
94 | message:
95 | "An error occured. This may be because you do not have internet access or because the shortcutsweb.app servers are unreachable."
96 | });
97 | };
98 | xhttp.onreadystatechange = () => {
99 | if (xhttp.readyState === 4) {
100 | onProgress && onProgress(1);
101 | // Typical action to be performed when the document is ready:
102 | if (xhttp.response) {
103 | const result: ArrayBuffer = xhttp.response;
104 | if (xhttp.status === 200) {
105 | return resolve({ result: "success", shortcut: result });
106 | }
107 | return resolve({
108 | result: "error",
109 | message: `There was an error getting your shortcut data. Maybe you entered the wrong URL? Additional information: Error ${
110 | xhttp.status
111 | }: ${xhttp.statusText}: ${Buffer.from(result).toString(
112 | "utf-8"
113 | )}`
114 | });
115 | }
116 | }
117 | };
118 |
119 | xhttp.send();
120 | });
121 | }
122 |
--------------------------------------------------------------------------------
/src/ace/mode-scpl.ts:
--------------------------------------------------------------------------------
1 | /*eslint-disable func-names*/
2 |
3 | import ace from "brace";
4 |
5 | // @ts-ignore
6 | ace.define("ace/mode/scpl_highlight_rules", function(
7 | require: any,
8 | exports: any,
9 | _module: any
10 | ) {
11 | "use strict";
12 | const oop = require("../lib/oop");
13 | const TextHighlightRules = require("./text_highlight_rules")
14 | .TextHighlightRules;
15 | /* --------------------- START ----------------------------- */
16 | const ScplHighlightRules = function() {
17 | // @ts-ignore
18 | this.$rules = {
19 | start: [
20 | {
21 | token: "punctuation",
22 | regex: '(\\")',
23 | push: "string__1"
24 | },
25 | {
26 | token: "punctuation",
27 | regex: "(\\')",
28 | push: "string__2"
29 | },
30 | {
31 | token: "punctuation",
32 | regex: "(^\\s*\\|\\s*)",
33 | push: "barlist__1"
34 | },
35 | {
36 | token: "variable",
37 | regex: "(([A-Za-z0-9@._]+):([A-Za-z0-9@._]+))"
38 | },
39 | {
40 | token: "support.type",
41 | regex: "(([A-Za-z0-9@._]+)=)"
42 | },
43 | {
44 | token: "support.constant",
45 | regex: "(@(([A-Za-z0-9@._]+))?)"
46 | },
47 | {
48 | token: "keyword",
49 | regex:
50 | "([Ii][Ff]|[Rr][Ee][Pp][Ee][Aa][Tt][Ww][Ii][Tt][Hh][Ee][Aa][Cc][Hh]|[Cc][Hh][Oo][Oo][Ss][Ee][Ff][Rr][Oo][Mm][Mm][Ee][Nn][Uu]|[Rr][Ee][Pp][Ee][Aa][Tt]|[Oo][Tt][Hh][Ee][Rr][Ww][Ii][Ss][Ee]|[Ee][Nn][Dd]|[Ff][Ll][Oo][Ww]|[Ee][Ll][Ss][Ee]|[Cc][Aa][Ss][Ee])"
51 | },
52 | {
53 | token: "entity.name.function",
54 | regex: "([A-Za-z0-9@._]+)"
55 | },
56 | {
57 | token: "comment",
58 | regex: "(/\\*)",
59 | push: "multi_line_comment__1"
60 | },
61 | {
62 | token: "comment",
63 | regex: "(--\\[)",
64 | push: "multi_line_comment__2"
65 | },
66 | {
67 | token: "comment",
68 | regex: "(--.*)"
69 | },
70 | {
71 | token: "comment",
72 | regex: "(#.*)"
73 | },
74 | {
75 | token: "comment",
76 | regex: "(//.*)"
77 | },
78 | {
79 | token: "punctuation",
80 | regex: "(\\()",
81 | push: "action__1"
82 | },
83 | {
84 | token: "punctuation",
85 | regex: "(\\[)",
86 | push: "action__2"
87 | },
88 | {
89 | token: "punctuation",
90 | regex: "({)",
91 | push: "action__3"
92 | },
93 | {
94 | token: "punctuation",
95 | regex: '(["\\:,;^\\->=])'
96 | },
97 | {
98 | token: "invalid",
99 | regex: "(\\?\\?.+?\\?\\?)"
100 | },
101 | {
102 | token: "invalid",
103 | regex: "([^\\s])"
104 | },
105 | {
106 | defaultToken: "text"
107 | }
108 | ],
109 | action__1: [
110 | {
111 | token: "punctuation",
112 | regex: "(\\))",
113 | next: "pop"
114 | },
115 | {
116 | token: "punctuation",
117 | regex: '(\\")',
118 | push: "string__1"
119 | },
120 | {
121 | token: "punctuation",
122 | regex: "(\\')",
123 | push: "string__2"
124 | },
125 | {
126 | token: "punctuation",
127 | regex: "(^\\s*\\|\\s*)",
128 | push: "barlist__1"
129 | },
130 | {
131 | token: "variable",
132 | regex: "(([A-Za-z0-9@._]+):([A-Za-z0-9@._]+))"
133 | },
134 | {
135 | token: "support.type",
136 | regex: "(([A-Za-z0-9@._]+)=)"
137 | },
138 | {
139 | token: "support.constant",
140 | regex: "(@(([A-Za-z0-9@._]+))?)"
141 | },
142 | {
143 | token: "keyword",
144 | regex:
145 | "([Ii][Ff]|[Rr][Ee][Pp][Ee][Aa][Tt][Ww][Ii][Tt][Hh][Ee][Aa][Cc][Hh]|[Cc][Hh][Oo][Oo][Ss][Ee][Ff][Rr][Oo][Mm][Mm][Ee][Nn][Uu]|[Rr][Ee][Pp][Ee][Aa][Tt]|[Oo][Tt][Hh][Ee][Rr][Ww][Ii][Ss][Ee]|[Ee][Nn][Dd]|[Ff][Ll][Oo][Ww]|[Ee][Ll][Ss][Ee]|[Cc][Aa][Ss][Ee])"
146 | },
147 | {
148 | token: "entity.name.function",
149 | regex: "([A-Za-z0-9@._]+)"
150 | },
151 | {
152 | token: "comment",
153 | regex: "(/\\*)",
154 | push: "multi_line_comment__1"
155 | },
156 | {
157 | token: "comment",
158 | regex: "(--\\[)",
159 | push: "multi_line_comment__2"
160 | },
161 | {
162 | token: "comment",
163 | regex: "(--.*)"
164 | },
165 | {
166 | token: "comment",
167 | regex: "(#.*)"
168 | },
169 | {
170 | token: "comment",
171 | regex: "(//.*)"
172 | },
173 | {
174 | token: "punctuation",
175 | regex: "(\\()",
176 | push: "action__1"
177 | },
178 | {
179 | token: "punctuation",
180 | regex: "(\\[)",
181 | push: "action__2"
182 | },
183 | {
184 | token: "punctuation",
185 | regex: "({)",
186 | push: "action__3"
187 | },
188 | {
189 | token: "punctuation",
190 | regex: '(["\\:,;^\\->=])'
191 | },
192 | {
193 | token: "invalid",
194 | regex: "(\\?\\?.+?\\?\\?)"
195 | },
196 | {
197 | token: "invalid",
198 | regex: "([^\\s])"
199 | },
200 | {
201 | defaultToken: "text"
202 | }
203 | ],
204 | action__2: [
205 | {
206 | token: "punctuation",
207 | regex: "(\\])",
208 | next: "pop"
209 | },
210 | {
211 | token: "punctuation",
212 | regex: '(\\")',
213 | push: "string__1"
214 | },
215 | {
216 | token: "punctuation",
217 | regex: "(\\')",
218 | push: "string__2"
219 | },
220 | {
221 | token: "punctuation",
222 | regex: "(^\\s*\\|\\s*)",
223 | push: "barlist__1"
224 | },
225 | {
226 | token: "variable",
227 | regex: "(([A-Za-z0-9@._]+):([A-Za-z0-9@._]+))"
228 | },
229 | {
230 | token: "support.type",
231 | regex: "(([A-Za-z0-9@._]+)=)"
232 | },
233 | {
234 | token: "support.constant",
235 | regex: "(@(([A-Za-z0-9@._]+))?)"
236 | },
237 | {
238 | token: "keyword",
239 | regex:
240 | "([Ii][Ff]|[Rr][Ee][Pp][Ee][Aa][Tt][Ww][Ii][Tt][Hh][Ee][Aa][Cc][Hh]|[Cc][Hh][Oo][Oo][Ss][Ee][Ff][Rr][Oo][Mm][Mm][Ee][Nn][Uu]|[Rr][Ee][Pp][Ee][Aa][Tt]|[Oo][Tt][Hh][Ee][Rr][Ww][Ii][Ss][Ee]|[Ee][Nn][Dd]|[Ff][Ll][Oo][Ww]|[Ee][Ll][Ss][Ee]|[Cc][Aa][Ss][Ee])"
241 | },
242 | {
243 | token: "entity.name.function",
244 | regex: "([A-Za-z0-9@._]+)"
245 | },
246 | {
247 | token: "comment",
248 | regex: "(/\\*)",
249 | push: "multi_line_comment__1"
250 | },
251 | {
252 | token: "comment",
253 | regex: "(--\\[)",
254 | push: "multi_line_comment__2"
255 | },
256 | {
257 | token: "comment",
258 | regex: "(--.*)"
259 | },
260 | {
261 | token: "comment",
262 | regex: "(#.*)"
263 | },
264 | {
265 | token: "comment",
266 | regex: "(//.*)"
267 | },
268 | {
269 | token: "punctuation",
270 | regex: "(\\()",
271 | push: "action__1"
272 | },
273 | {
274 | token: "punctuation",
275 | regex: "(\\[)",
276 | push: "action__2"
277 | },
278 | {
279 | token: "punctuation",
280 | regex: "({)",
281 | push: "action__3"
282 | },
283 | {
284 | token: "punctuation",
285 | regex: '(["\\:,;^\\->=])'
286 | },
287 | {
288 | token: "invalid",
289 | regex: "(\\?\\?.+?\\?\\?)"
290 | },
291 | {
292 | token: "invalid",
293 | regex: "([^\\s])"
294 | },
295 | {
296 | defaultToken: "text"
297 | }
298 | ],
299 | action__3: [
300 | {
301 | token: "punctuation",
302 | regex: "(})",
303 | next: "pop"
304 | },
305 | {
306 | token: "punctuation",
307 | regex: '(\\")',
308 | push: "string__1"
309 | },
310 | {
311 | token: "punctuation",
312 | regex: "(\\')",
313 | push: "string__2"
314 | },
315 | {
316 | token: "punctuation",
317 | regex: "(^\\s*\\|\\s*)",
318 | push: "barlist__1"
319 | },
320 | {
321 | token: "variable",
322 | regex: "(([A-Za-z0-9@._]+):([A-Za-z0-9@._]+))"
323 | },
324 | {
325 | token: "support.type",
326 | regex: "(([A-Za-z0-9@._]+)=)"
327 | },
328 | {
329 | token: "support.constant",
330 | regex: "(@(([A-Za-z0-9@._]+))?)"
331 | },
332 | {
333 | token: "keyword",
334 | regex:
335 | "([Ii][Ff]|[Rr][Ee][Pp][Ee][Aa][Tt][Ww][Ii][Tt][Hh][Ee][Aa][Cc][Hh]|[Cc][Hh][Oo][Oo][Ss][Ee][Ff][Rr][Oo][Mm][Mm][Ee][Nn][Uu]|[Rr][Ee][Pp][Ee][Aa][Tt]|[Oo][Tt][Hh][Ee][Rr][Ww][Ii][Ss][Ee]|[Ee][Nn][Dd]|[Ff][Ll][Oo][Ww]|[Ee][Ll][Ss][Ee]|[Cc][Aa][Ss][Ee])"
336 | },
337 | {
338 | token: "entity.name.function",
339 | regex: "([A-Za-z0-9@._]+)"
340 | },
341 | {
342 | token: "comment",
343 | regex: "(/\\*)",
344 | push: "multi_line_comment__1"
345 | },
346 | {
347 | token: "comment",
348 | regex: "(--\\[)",
349 | push: "multi_line_comment__2"
350 | },
351 | {
352 | token: "comment",
353 | regex: "(--.*)"
354 | },
355 | {
356 | token: "comment",
357 | regex: "(#.*)"
358 | },
359 | {
360 | token: "comment",
361 | regex: "(//.*)"
362 | },
363 | {
364 | token: "punctuation",
365 | regex: "(\\()",
366 | push: "action__1"
367 | },
368 | {
369 | token: "punctuation",
370 | regex: "(\\[)",
371 | push: "action__2"
372 | },
373 | {
374 | token: "punctuation",
375 | regex: "({)",
376 | push: "action__3"
377 | },
378 | {
379 | token: "punctuation",
380 | regex: '(["\\:,;^\\->=])'
381 | },
382 | {
383 | token: "invalid",
384 | regex: "(\\?\\?.+?\\?\\?)"
385 | },
386 | {
387 | token: "invalid",
388 | regex: "([^\\s])"
389 | },
390 | {
391 | defaultToken: "text"
392 | }
393 | ],
394 | barlist__1: [
395 | {
396 | token: "punctuation",
397 | regex: "($)",
398 | next: "pop"
399 | },
400 | {
401 | token: "escape",
402 | regex: "(\\\\[\"'\\\\n])"
403 | },
404 | {
405 | token: "escape",
406 | regex: "(\\\\\\()",
407 | push: "escape__1"
408 | },
409 | {
410 | token: "support.constant",
411 | regex: "([^\\\\]+)"
412 | },
413 | {
414 | defaultToken: "text"
415 | }
416 | ],
417 | escape__1: [
418 | {
419 | token: "escape",
420 | regex: "(\\))",
421 | next: "pop"
422 | },
423 | {
424 | token: "punctuation",
425 | regex: '(\\")',
426 | push: "string__1"
427 | },
428 | {
429 | token: "punctuation",
430 | regex: "(\\')",
431 | push: "string__2"
432 | },
433 | {
434 | token: "punctuation",
435 | regex: "(^\\s*\\|\\s*)",
436 | push: "barlist__1"
437 | },
438 | {
439 | token: "variable",
440 | regex: "(([A-Za-z0-9@._]+):([A-Za-z0-9@._]+))"
441 | },
442 | {
443 | token: "support.type",
444 | regex: "(([A-Za-z0-9@._]+)=)"
445 | },
446 | {
447 | token: "support.constant",
448 | regex: "(@(([A-Za-z0-9@._]+))?)"
449 | },
450 | {
451 | token: "keyword",
452 | regex:
453 | "([Ii][Ff]|[Rr][Ee][Pp][Ee][Aa][Tt][Ww][Ii][Tt][Hh][Ee][Aa][Cc][Hh]|[Cc][Hh][Oo][Oo][Ss][Ee][Ff][Rr][Oo][Mm][Mm][Ee][Nn][Uu]|[Rr][Ee][Pp][Ee][Aa][Tt]|[Oo][Tt][Hh][Ee][Rr][Ww][Ii][Ss][Ee]|[Ee][Nn][Dd]|[Ff][Ll][Oo][Ww]|[Ee][Ll][Ss][Ee]|[Cc][Aa][Ss][Ee])"
454 | },
455 | {
456 | token: "entity.name.function",
457 | regex: "([A-Za-z0-9@._]+)"
458 | },
459 | {
460 | token: "comment",
461 | regex: "(/\\*)",
462 | push: "multi_line_comment__1"
463 | },
464 | {
465 | token: "comment",
466 | regex: "(--\\[)",
467 | push: "multi_line_comment__2"
468 | },
469 | {
470 | token: "comment",
471 | regex: "(--.*)"
472 | },
473 | {
474 | token: "comment",
475 | regex: "(#.*)"
476 | },
477 | {
478 | token: "comment",
479 | regex: "(//.*)"
480 | },
481 | {
482 | token: "punctuation",
483 | regex: "(\\()",
484 | push: "action__1"
485 | },
486 | {
487 | token: "punctuation",
488 | regex: "(\\[)",
489 | push: "action__2"
490 | },
491 | {
492 | token: "punctuation",
493 | regex: "({)",
494 | push: "action__3"
495 | },
496 | {
497 | token: "punctuation",
498 | regex: '(["\\:,;^\\->=])'
499 | },
500 | {
501 | token: "invalid",
502 | regex: "(\\?\\?.+?\\?\\?)"
503 | },
504 | {
505 | token: "invalid",
506 | regex: "([^\\s])"
507 | },
508 | {
509 | defaultToken: "text"
510 | }
511 | ],
512 | multi_line_comment__1: [
513 | {
514 | token: "comment",
515 | regex: "(\\*/)",
516 | next: "pop"
517 | },
518 | {
519 | defaultToken: "comment"
520 | }
521 | ],
522 | multi_line_comment__2: [
523 | {
524 | token: "comment",
525 | regex: "(--\\])",
526 | next: "pop"
527 | },
528 | {
529 | defaultToken: "comment"
530 | }
531 | ],
532 | string__1: [
533 | {
534 | token: "punctuation",
535 | regex: '(\\")',
536 | next: "pop"
537 | },
538 | {
539 | token: "escape",
540 | regex: "(\\\\[\"'\\\\n])"
541 | },
542 | {
543 | token: "escape",
544 | regex: "(\\\\\\()",
545 | push: "escape__1"
546 | },
547 | {
548 | token: "support.constant",
549 | regex: '([^"\\\\]+)'
550 | },
551 | {
552 | defaultToken: "text"
553 | }
554 | ],
555 | string__2: [
556 | {
557 | token: "punctuation",
558 | regex: "(\\')",
559 | next: "pop"
560 | },
561 | {
562 | token: "escape",
563 | regex: "(\\\\[\"'\\\\n])"
564 | },
565 | {
566 | token: "escape",
567 | regex: "(\\\\\\()",
568 | push: "escape__1"
569 | },
570 | {
571 | token: "support.constant",
572 | regex: "([^'\\\\]+)"
573 | },
574 | {
575 | defaultToken: "text"
576 | }
577 | ]
578 | };
579 | // @ts-ignore
580 | this.normalizeRules();
581 | };
582 | /* ------------------------ END ------------------------------ */
583 | oop.inherits(ScplHighlightRules, TextHighlightRules);
584 | exports.ScplHighlightRules = ScplHighlightRules;
585 | });
586 |
587 | // @ts-ignore
588 | ace.define("ace/mode/scpl", ["ace/mode/scpl_highlight_rules"], function(
589 | // @ts-ignore
590 | require,
591 | // @ts-ignore
592 | exports,
593 | // @ts-ignore
594 | _module
595 | ) {
596 | const oop = require("ace/lib/oop");
597 | const TextMode = require("ace/mode/text").Mode;
598 | const ExampleHighlightRules = require("ace/mode/scpl_highlight_rules")
599 | .ScplHighlightRules;
600 |
601 | const Mode = function() {
602 | // @ts-ignore
603 | this.HighlightRules = ExampleHighlightRules;
604 | };
605 | oop.inherits(Mode, TextMode);
606 |
607 | (function() {
608 | // Extra logic goes here. (see below)
609 | }.call(Mode.prototype));
610 |
611 | exports.Mode = Mode;
612 | });
613 | // (function() {
614 | // // @ts-ignore
615 | // ace.require(["ace/mode/scpl"], function(m) {
616 | // if (typeof module === "object" && typeof exports === "object" && module) {
617 | // module.exports = m;
618 | // }
619 | // });
620 | // }());
621 |
--------------------------------------------------------------------------------
/src/ace/scpl.iro:
--------------------------------------------------------------------------------
1 | #################################################################
2 | ## Iro
3 | ################################################################
4 | ##
5 | ## * Press Ctrl + '+'/'-' To Zoom in
6 | ## * Press Ctrl + S to save and recalculate...
7 | ## * Documents are saved to web storage.
8 | ## * Only one save slot supported.
9 | ## * Matches cannot span lines.
10 | ## * Unicode chars must be defined in \u0000 to \uffff format.
11 | ## * All matches must be contained by a single group ( ... )
12 | ## * Look behinds not permitted, (?<= or (?=])
181 | styles [] = .punctuation;
182 | }
183 | : pattern {
184 | regex \= (\?\?.+?\?\?)
185 | styles [] = .illegal;
186 | }
187 | : pattern {
188 | regex \= ([^\s])
189 | styles [] = .illegal;
190 | }
191 | }
192 | string : context {
193 | : inline_push {
194 | regex \= (\")
195 | styles [] = .punctuation;
196 | : pop {
197 | regex \= (\")
198 | styles [] = .punctuation;
199 | }
200 | : include "escape";
201 | : pattern{
202 | regex \= ([^"\\]+)
203 | styles [] = .text;
204 | }
205 | }
206 | : inline_push {
207 | regex \= (\')
208 | styles [] = .punctuation;
209 | : pop {
210 | regex \= (\')
211 | styles [] = .punctuation;
212 | }
213 | : include "escape";
214 | : pattern{
215 | regex \= ([^'\\]+)
216 | styles [] = .text;
217 | }
218 | }
219 | }
220 | barlist : context {
221 | : inline_push {
222 | regex \= (^\s*\|\s*)
223 | styles [] = .punctuation;
224 | : pop {
225 | regex \= ($)
226 | styles [] = .punctuation;
227 | }
228 | : include "escape";
229 | : pattern{
230 | regex \= ([^\\]+)
231 | styles [] = .text;
232 | }
233 | }
234 | }
235 | escape : context {
236 | : pattern {
237 | regex \= (\\["'\\n])
238 | styles [] = .escape;
239 | }
240 | : inline_push {
241 | regex \= (\\\()
242 | styles [] = .escape;
243 | : pop {
244 | regex \= (\))
245 | styles [] = .escape;
246 | }
247 | : include "action";
248 | }
249 | }
250 |
251 |
252 | multi_line_comment : context {
253 | description = multiline
254 | : inline_push {
255 | regex \= (/\*)
256 | styles [] = .comment;
257 | default_style = .comment
258 | : pop {
259 | regex \= (\*/)
260 | styles [] = .comment;
261 | }
262 | }
263 | : inline_push {
264 | regex \= (--\[)
265 | styles [] = .comment;
266 | default_style = .comment
267 | : pop {
268 | regex \= (--\])
269 | styles [] = .comment;
270 | }
271 | }
272 | }
273 |
274 | inline_comment : context {
275 | : pattern {
276 | regex \= (--.*)
277 | styles [] = .comment;
278 | }
279 | : pattern {
280 | regex \= (#.*)
281 | styles [] = .comment;
282 | }
283 | : pattern {
284 | regex \= (//.*)
285 | styles [] = .comment;
286 | }
287 | }
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/src/data/ActionCategories.ts:
--------------------------------------------------------------------------------
1 | export const categories: {[key: string]: string | undefined} = {
2 | "Photos & Video": "action-item-photos",
3 | "Scripting": "action-item-gear",
4 | "Calendar": "action-item-calendar",
5 | "Documents": "action-item-files",
6 | "Music": "action-item-music",
7 | "Sharing": "action-item-sharing",
8 | "Text": "action-item-text",
9 | "Web": "action-item-web",
10 | "Contacts": "action-item-contacts",
11 | "Location": "action-item-location"
12 | };
13 |
--------------------------------------------------------------------------------
/src/data/ShortcutMeta.ts:
--------------------------------------------------------------------------------
1 | export type GlyphName =
2 | | "car"
3 | | "amb"
4 | | "house"
5 | | "cart"
6 | | "forkknife"
7 | | "sun"
8 | | "cloud"
9 | | "tree"
10 | | "footprints"
11 | | "compass"
12 | | "photo"
13 | | "bus"
14 | | "plane"
15 | | "hospital"
16 | | "purse"
17 | | "gaspump"
18 | | "moon"
19 | | "rain"
20 | | "flower"
21 | | "signs"
22 | | "earth"
23 | | "film"
24 | | "motorcycle"
25 | | "boat"
26 | | "city"
27 | | "stand"
28 | | "temp"
29 | | "snow"
30 | | "umbrella"
31 | | "fire"
32 | | "binoculars"
33 | | "mountain"
34 | | "filmfull"
35 | | "camera"
36 | | "videomarker"
37 | | "calendar"
38 | | "comment"
39 | | "paperairplane"
40 | | "creditcard"
41 | | "smartphone"
42 | | "emptykeyboard"
43 | | "printer"
44 | | "database"
45 | | "cube"
46 | | "videocamera"
47 | | "playbutton-one"
48 | | "message"
49 | | "letter"
50 | | "suitcase"
51 | | "watch"
52 | | "laptop"
53 | | "calculator"
54 | | "harddrive"
55 | | "servers"
56 | | "television"
57 | | "microphone"
58 | | "clipboard"
59 | | "messages"
60 | | "openletter"
61 | | "folder"
62 | | "phone"
63 | | "keyboard"
64 | | "stats"
65 | | "serverset"
66 | | "inbox"
67 | | "controller"
68 | | "puzzle"
69 | | "speaker"
70 | | "bookmark"
71 | | "mask"
72 | | "dice"
73 | | "soccer"
74 | | "lifesaver"
75 | | "chess"
76 | | "stopwatch"
77 | | "platter"
78 | | "trophy"
79 | | "headphones"
80 | | "books"
81 | | "emptyglasses"
82 | | "ticket"
83 | | "baseball"
84 | | "tennisball"
85 | | "telescope"
86 | | "clock"
87 | | "volume"
88 | | "heart"
89 | | "lightbulb"
90 | | "musicnote"
91 | | "book"
92 | | "glasses"
93 | | "masks"
94 | | "basketball"
95 | | "football"
96 | | "microscope"
97 | | "alarmclock"
98 | | "bell"
99 | | "star"
100 | | "lightning"
101 | | "flag"
102 | | "hourglass"
103 | | "battery"
104 | | "paintbrush"
105 | | "scissors"
106 | | "colorpicker"
107 | | "hammerwrench"
108 | | "screwdriver"
109 | | "trashcan"
110 | | "soupbowl"
111 | | "fish"
112 | | "tag"
113 | | "locked"
114 | | "magicwand"
115 | | "pencil"
116 | | "magnify"
117 | | "tool"
118 | | "gears"
119 | | "hand"
120 | | "teardrop"
121 | | "apple"
122 | | "cake"
123 | | "key"
124 | | "unlocked"
125 | | "magicstar"
126 | | "paperclip"
127 | | "link"
128 | | "wrench"
129 | | "hammer"
130 | | "privacy"
131 | | "cup"
132 | | "carrot"
133 | | "bottle"
134 | | "wineglass"
135 | | "oven"
136 | | "showerhead"
137 | | "pillbottle"
138 | | "scope"
139 | | "beaker"
140 | | "pawprint"
141 | | "gift"
142 | | "stairs"
143 | | "hanger"
144 | | "shirt"
145 | | "pill"
146 | | "bandaid"
147 | | "needle"
148 | | "cat"
149 | | "like"
150 | | "alien"
151 | | "rocket"
152 | | "laundry"
153 | | "bath"
154 | | "pills"
155 | | "inhaler"
156 | | "atom"
157 | | "dog"
158 | | "cap"
159 | | "bed"
160 | | "girlbaby"
161 | | "mansymbol"
162 | | "user"
163 | | "accessibility"
164 | | "dance"
165 | | "snowboard"
166 | | "activity"
167 | | "boybaby"
168 | | "womansymbol"
169 | | "users"
170 | | "podium"
171 | | "gym"
172 | | "swim"
173 | | "sprint"
174 | | "person"
175 | | "handicap"
176 | | "group"
177 | | "handraised"
178 | | "hike"
179 | | "hiking"
180 | | "cane"
181 | | "alert"
182 | | "bookmarkthis"
183 | | "stopfilled"
184 | | "left"
185 | | "up"
186 | | "play"
187 | | "stop"
188 | | "checked"
189 | | "moneysign"
190 | | "yensign"
191 | | "info"
192 | | "shareleft"
193 | | "barcode"
194 | | "frame"
195 | | "right"
196 | | "down"
197 | | "prev"
198 | | "next"
199 | | "plus"
200 | | "eurosign"
201 | | "bitcoinsign"
202 | | "smile"
203 | | "shareright"
204 | | "qrcode"
205 | | "sizes"
206 | | "download"
207 | | "upload"
208 | | "power"
209 | | "help"
210 | | "xfilled"
211 | | "pounds"
212 | | "pi"
213 | | "cssfile"
214 | | "money"
215 | | "yen"
216 | | "filefilled"
217 | | "list"
218 | | "more"
219 | | "share"
220 | | "spinner"
221 | | "target"
222 | | "location"
223 | | "crop"
224 | | "move"
225 | | "euro"
226 | | "bitcoin"
227 | | "file"
228 | | "document"
229 | | "listitems"
230 | | "infinite"
231 | | "loading"
232 | | "podcasts"
233 | | "mapmarker"
234 | | "exit"
235 | | "repeat"
236 | | "poundsign"
237 | | "asterisk"
238 | | "filedoc"
239 | | "fourgrid"
240 | | "sixgrid"
241 | | "recycle"
242 | | "playvideo"
243 | | "bigtarget"
244 | | "squarep"
245 | | "resize"
246 | | "sync"
247 | | "playsolo"
248 | | "rss"
249 | | "quotes"
250 | | "text"
251 | | "shuffle"
252 | | "signal"
253 | | "peace"
254 | | "cloudservice"
255 | | "settings"
256 | | "wifi"
257 | | "nuclear";
258 |
259 | export const objectGlyphs: GlyphName[] = [
260 | "car",
261 | "amb",
262 | "house",
263 | "cart",
264 | "forkknife",
265 | "sun",
266 | "cloud",
267 | "tree",
268 | "footprints",
269 | "compass",
270 | "photo",
271 | "bus",
272 | "plane",
273 | "hospital",
274 | "purse",
275 | "gaspump",
276 | "moon",
277 | "rain",
278 | "flower",
279 | "signs",
280 | "earth",
281 | "film",
282 | "motorcycle",
283 | "boat",
284 | "city",
285 | "stand",
286 | "temp",
287 | "snow",
288 | "umbrella",
289 | "fire",
290 | "binoculars",
291 | "mountain",
292 | "filmfull",
293 | "camera",
294 | "videomarker",
295 | "calendar",
296 | "comment",
297 | "paperairplane",
298 | "creditcard",
299 | "smartphone",
300 | "emptykeyboard",
301 | "printer",
302 | "database",
303 | "cube",
304 | "videocamera",
305 | "playbutton-one",
306 | "message",
307 | "letter",
308 | "suitcase",
309 | "watch",
310 | "laptop",
311 | "calculator",
312 | "harddrive",
313 | "servers",
314 | "television",
315 | "microphone",
316 | "clipboard",
317 | "messages",
318 | "openletter",
319 | "folder",
320 | "phone",
321 | "keyboard",
322 | "stats",
323 | "serverset",
324 | "inbox",
325 | "controller",
326 | "puzzle",
327 | "speaker",
328 | "bookmark",
329 | "mask",
330 | "dice",
331 | "soccer",
332 | "lifesaver",
333 | "chess",
334 | "stopwatch",
335 | "platter",
336 | "trophy",
337 | "headphones",
338 | "books",
339 | "emptyglasses",
340 | "ticket",
341 | "baseball",
342 | "tennisball",
343 | "telescope",
344 | "clock",
345 | "volume",
346 | "heart",
347 | "lightbulb",
348 | "musicnote",
349 | "book",
350 | "glasses",
351 | "masks",
352 | "basketball",
353 | "football",
354 | "microscope",
355 | "alarmclock",
356 | "bell",
357 | "star",
358 | "lightning",
359 | "flag",
360 | "hourglass",
361 | "battery",
362 | "paintbrush",
363 | "scissors",
364 | "colorpicker",
365 | "hammerwrench",
366 | "screwdriver",
367 | "trashcan",
368 | "soupbowl",
369 | "fish",
370 | "tag",
371 | "locked",
372 | "magicwand",
373 | "pencil",
374 | "magnify",
375 | "tool",
376 | "gears",
377 | "hand",
378 | "teardrop",
379 | "apple",
380 | "cake",
381 | "key",
382 | "unlocked",
383 | "magicstar",
384 | "paperclip",
385 | "link",
386 | "wrench",
387 | "hammer",
388 | "privacy",
389 | "cup",
390 | "carrot",
391 | "bottle",
392 | "wineglass",
393 | "oven",
394 | "showerhead",
395 | "pillbottle",
396 | "scope",
397 | "beaker",
398 | "pawprint",
399 | "gift",
400 | "stairs",
401 | "hanger",
402 | "shirt",
403 | "pill",
404 | "bandaid",
405 | "needle",
406 | "cat",
407 | "like",
408 | "alien",
409 | "rocket",
410 | "laundry",
411 | "bath",
412 | "pills",
413 | "inhaler",
414 | "atom",
415 | "dog",
416 | "cap",
417 | "bed"
418 | ];
419 |
420 | export const peopleGlyphs: GlyphName[] = [
421 | "girlbaby",
422 | "mansymbol",
423 | "user",
424 | "accessibility",
425 | "dance",
426 | "snowboard",
427 | "activity",
428 | "boybaby",
429 | "womansymbol",
430 | "users",
431 | "podium",
432 | "gym",
433 | "swim",
434 | "sprint",
435 | "person",
436 | "handicap",
437 | "group",
438 | "handraised",
439 | "hike",
440 | "hiking",
441 | "cane"
442 | ];
443 |
444 | export const symbolGlyphs: GlyphName[] = [
445 | "alert",
446 | "bookmarkthis",
447 | "stopfilled",
448 | "left",
449 | "up",
450 | "play",
451 | "stop",
452 | "checked",
453 | "moneysign",
454 | "yensign",
455 | "info",
456 | "shareleft",
457 | "barcode",
458 | "frame",
459 | "right",
460 | "down",
461 | "prev",
462 | "next",
463 | "plus",
464 | "eurosign",
465 | "bitcoinsign",
466 | "smile",
467 | "shareright",
468 | "qrcode",
469 | "sizes",
470 | "download",
471 | "upload",
472 | "power",
473 | "help",
474 | "xfilled",
475 | "pounds",
476 | "pi",
477 | "cssfile",
478 | "money",
479 | "yen",
480 | "filefilled",
481 | "list",
482 | "more",
483 | "share",
484 | "spinner",
485 | "target",
486 | "location",
487 | "crop",
488 | "move",
489 | "euro",
490 | "bitcoin",
491 | "file",
492 | "document",
493 | "listitems",
494 | "infinite",
495 | "loading",
496 | "podcasts",
497 | "mapmarker",
498 | "exit",
499 | "repeat",
500 | "poundsign",
501 | "asterisk",
502 | "filedoc",
503 | "fourgrid",
504 | "sixgrid",
505 | "recycle",
506 | "playvideo",
507 | "bigtarget",
508 | "squarep",
509 | "resize",
510 | "sync",
511 | "playsolo",
512 | "rss",
513 | "quotes",
514 | "text",
515 | "shuffle",
516 | "signal",
517 | "peace",
518 | "cloudservice",
519 | "settings",
520 | "wifi",
521 | "nuclear"
522 | ];
523 |
524 | export type ColorName =
525 | | "red"
526 | | "darkorange"
527 | | "orange"
528 | | "yellow"
529 | | "green"
530 | | "seagreen"
531 | | "lightblue"
532 | | "blue"
533 | | "darkblue"
534 | | "darkpurple"
535 | | "purple"
536 | | "pink"
537 | | "black"
538 | | "brown"
539 | | "grey";
540 |
541 | export const colors: ColorName[] = [
542 | "red",
543 | "darkorange",
544 | "orange",
545 | "yellow",
546 | "green",
547 | "seagreen",
548 | "lightblue",
549 | "blue",
550 | "darkblue",
551 | "darkpurple",
552 | "purple",
553 | "pink",
554 | "black",
555 | "brown",
556 | "grey"
557 | ];
558 |
--------------------------------------------------------------------------------
/src/img/back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
47 |
--------------------------------------------------------------------------------
/src/img/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/bg.jpg
--------------------------------------------------------------------------------
/src/img/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/img/delete.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/src/img/delete_w.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/src/img/down.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
43 |
--------------------------------------------------------------------------------
/src/img/edit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/img/edit_w.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/img/error.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/img/error_grey.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/img/error_w.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/img/error_x.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/src/img/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/file.png
--------------------------------------------------------------------------------
/src/img/file_active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/file_active.png
--------------------------------------------------------------------------------
/src/img/file_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/file_error.png
--------------------------------------------------------------------------------
/src/img/file_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/file_warning.png
--------------------------------------------------------------------------------
/src/img/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/folder.png
--------------------------------------------------------------------------------
/src/img/folder_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/folder_error.png
--------------------------------------------------------------------------------
/src/img/folder_warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/folder_warning.png
--------------------------------------------------------------------------------
/src/img/glyph_sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/glyph_sheet.png
--------------------------------------------------------------------------------
/src/img/glyph_sheet_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/glyph_sheet_white.png
--------------------------------------------------------------------------------
/src/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/icon.png
--------------------------------------------------------------------------------
/src/img/import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/import.png
--------------------------------------------------------------------------------
/src/img/magic.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
80 |
--------------------------------------------------------------------------------
/src/img/magic_w.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
80 |
--------------------------------------------------------------------------------
/src/img/menu.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/src/img/new_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/new_file.png
--------------------------------------------------------------------------------
/src/img/new_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/new_folder.png
--------------------------------------------------------------------------------
/src/img/right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
43 |
--------------------------------------------------------------------------------
/src/img/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
5 |
--------------------------------------------------------------------------------
/src/img/settings.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
--------------------------------------------------------------------------------
/src/img/settings_w.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
--------------------------------------------------------------------------------
/src/img/shortcut-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/shortcut-file.png
--------------------------------------------------------------------------------
/src/img/shortcuts_grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/shortcuts_grey.png
--------------------------------------------------------------------------------
/src/img/success.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/img/upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/upload.png
--------------------------------------------------------------------------------
/src/img/var-sheet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pfgithub/scpl-editor/9e3d5e13a65d723362e1f76c300ea4b4bf621617/src/img/var-sheet.png
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0;
3 | margin: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import App from "./App";
5 | import * as serviceWorker from "./serviceWorker";
6 |
7 | ReactDOM.render(, document.getElementById("root"));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: https://bit.ly/CRA-PWA
12 | serviceWorker.register();
13 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === "localhost" ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === "[::1]" ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | type Config = {
24 | onSuccess?: (registration: ServiceWorkerRegistration) => void;
25 | onUpdate?: (registration: ServiceWorkerRegistration) => void;
26 | };
27 |
28 | export function register(config?: Config) {
29 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
30 | // The URL constructor is available in all browsers that support SW.
31 | const publicUrl = new URL(
32 | (process as { env: { [key: string]: string } }).env.PUBLIC_URL,
33 | window.location.href
34 | );
35 | if (publicUrl.origin !== window.location.origin) {
36 | // Our service worker won't work if PUBLIC_URL is on a different origin
37 | // from what our page is served on. This might happen if a CDN is used to
38 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
39 | return;
40 | }
41 |
42 | window.addEventListener("load", () => {
43 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
44 |
45 | if (isLocalhost) {
46 | // This is running on localhost. Let's check if a service worker still exists or not.
47 | checkValidServiceWorker(swUrl, config);
48 |
49 | // Add some additional logging to localhost, pointing developers to the
50 | // service worker/PWA documentation.
51 | navigator.serviceWorker.ready.then(() => {
52 | console.log(
53 | "This web app is being served cache-first by a service " +
54 | "worker. To learn more, visit https://bit.ly/CRA-PWA"
55 | );
56 | });
57 | } else {
58 | // Is not localhost. Just register service worker
59 | registerValidSW(swUrl, config);
60 | }
61 | });
62 | }
63 | }
64 |
65 | function registerValidSW(swUrl: string, config?: Config) {
66 | navigator.serviceWorker
67 | .register(swUrl)
68 | .then(registration => {
69 | registration.onupdatefound = () => {
70 | const installingWorker = registration.installing;
71 | if (installingWorker == null) {
72 | return;
73 | }
74 | installingWorker.onstatechange = () => {
75 | if (installingWorker.state === "installed") {
76 | if (navigator.serviceWorker.controller) {
77 | // At this point, the updated precached content has been fetched,
78 | // but the previous service worker will still serve the older
79 | // content until all client tabs are closed.
80 | console.log(
81 | "New content is available and will be used when all " +
82 | "tabs for this page are closed. See https://bit.ly/CRA-PWA."
83 | );
84 |
85 | // Execute callback
86 | if (config && config.onUpdate) {
87 | config.onUpdate(registration);
88 | }
89 | } else {
90 | // At this point, everything has been precached.
91 | // It's the perfect time to display a
92 | // "Content is cached for offline use." message.
93 | console.log("Content is cached for offline use.");
94 |
95 | // Execute callback
96 | if (config && config.onSuccess) {
97 | config.onSuccess(registration);
98 | }
99 | }
100 | }
101 | };
102 | };
103 | })
104 | .catch(error => {
105 | console.error("Error during service worker registration:", error);
106 | });
107 | }
108 |
109 | function checkValidServiceWorker(swUrl: string, config?: Config) {
110 | // Check if the service worker can be found. If it can't reload the page.
111 | fetch(swUrl)
112 | .then(response => {
113 | // Ensure service worker exists, and that we really are getting a JS file.
114 | const contentType = response.headers.get("content-type");
115 | if (
116 | response.status === 404 ||
117 | (contentType != null &&
118 | contentType.indexOf("javascript") === -1)
119 | ) {
120 | // No service worker found. Probably a different app. Reload the page.
121 | navigator.serviceWorker.ready.then(registration => {
122 | registration.unregister().then(() => {
123 | window.location.reload();
124 | });
125 | });
126 | } else {
127 | // Service worker found. Proceed as normal.
128 | registerValidSW(swUrl, config);
129 | }
130 | })
131 | .catch(() => {
132 | console.log(
133 | "No internet connection found. App is running in offline mode."
134 | );
135 | });
136 | }
137 |
138 | export function unregister() {
139 | if ("serviceWorker" in navigator) {
140 | navigator.serviceWorker.ready.then(registration => {
141 | registration.unregister();
142 | });
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/src/testshortcut.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "WFWorkflowClientVersion": "754",
4 | "WFWorkflowClientRelese": "2.1.2",
5 | "WFWorkflowMinimumClientVersion": 411,
6 | "WFWorkflowIcon": {
7 | "WFWorkflowIconStartColor": 2071128575,
8 | "WFWorkflowIconImageData": {
9 | "type": "Buffer",
10 | "data": []
11 | },
12 | "WFWorkflowIconGlyphNumber": 59511
13 | },
14 | "WFWorkflowTypes": ["NCWidget", "WatchKit"],
15 | "WFWorkflowInputContentItemClasses": [
16 | "WFAppStoreAppContentItem",
17 | "WFArticleContentItem",
18 | "WFContactContentItem",
19 | "WFDateContentItem",
20 | "WFEmailAddressContentItem",
21 | "WFGenericFileContentItem",
22 | "WFImageContentItem",
23 | "WFiTunesProductContentItem",
24 | "WFLocationContentItem",
25 | "WFDCMapsLinkContentItem",
26 | "WFAVAssetContentItem",
27 | "WFPDFContentItem",
28 | "WFPhoneNumberContentItem",
29 | "WFRichTextContentItem",
30 | "WFSafariWebPageContentItem",
31 | "WFStringContentItem",
32 | "WFURLContentItem"
33 | ],
34 | "WFWorkflowActions": []
35 | }
36 | ]
37 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "noEmit": true,
20 | "jsx": "preserve"
21 | },
22 | "include": [
23 | "src"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------