├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── index.d.ts ├── modules └── LineChart.js ├── parse-docs.js ├── scriptable.sh ├── tsconfig.json └── widgets ├── Crypto-Line-Chart.js └── InfoWidget.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .DS_Store 107 | sources 108 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "sources/.Trash": true, 4 | "sources/cache": true 5 | } 6 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Run", 8 | "type": "shell", 9 | "command": "open \"scriptable:///run/$(echo ${fileBasenameNoExtension} | sed 's/ /%20/g')\"", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | }, 14 | "presentation": { 15 | "reveal": "never", 16 | "focus": false 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Slavik Nychkalo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scriptable Development Environment 2 | 3 | 4 | This project includes 5 | - Scriptable types generated from documentation page 6 | - **RUN SCRIPT** hotkey: + SHIFT + B, it will run current script in Scriptable 7 | > Script file name should not contain spaces 8 | - Folder with all your Scriptable apps 9 | 10 | 11 | ## Getting started 12 | 13 | How to start to develop scriptable apps with VSCode: 14 | 15 | 1. Download [Scriptable for MacOS](https://web.archive.org/web/20220721061634/https://scriptable.app/mac-beta/Scriptable.zip) (via Internet Archive Wayback Machine) 16 | 2. Enable iCloud sync for Scriptable 17 | 3. Download [VSCode](https://code.visualstudio.com/) 18 | 4. Clone this repository 19 | ```sh 20 | git clone https://github.com/gebeto/scriptables 21 | ``` 22 | 5. Run command to initialize your sources folder link 23 | ```sh 24 | ./scriptable.sh init 25 | ``` 26 | 6. Done! Open VSCode in the repo(`code .`) and start to build your apps fast and easy! 27 | 28 | > Folder `sources` is your scriptable folder link, you can edit files there and it will be updated in scriptable app. 29 | 30 | 31 | ## Initialize your local env 32 | 33 | Tou can use BASH script or [VSCode extension](https://marketplace.visualstudio.com/items?itemName=gebeto.vscode-scriptable) for it 34 | 35 | ```sh 36 | $ ./scriptable.sh init 37 | ``` 38 | 39 | 40 | ## Import your script for git integration 41 | 42 | > IMPORTANT: Script name should not contain any **spaces**, because RUN hotkey will now working 43 | 44 | ```sh 45 | $ ./scriptable.sh import ScriptName 46 | # or 47 | $ ./scriptable.sh import Script-Name 48 | # or 49 | $ ./scriptable.sh import Script_Name 50 | ``` 51 | 52 | 53 | ## Relative projects 54 | - [https://github.com/gebeto/scriptable-vscode](https://github.com/gebeto/scriptable-vscode) 55 | > plugin will replace `scriptable.sh` when it will done(work in progress). 56 | - [https://github.com/schl3ck/ios-scriptable-types](https://github.com/schl3ck/ios-scriptable-types) 57 | > Scriptable Typescript typings 58 | 59 | 60 | ## ERRORS 61 | If scriptable typing are not loaded for you, need to add `///` on top of the your script (like shown below). 62 | Where **path** is a relative path to the `index.d.ts` file. 63 | 64 | ```diff 65 | // Variables used by Scriptable. 66 | // These must be at the very top of the file. Do not edit. 67 | // icon-color: green; icon-glyph: magic; 68 | + /// 69 | 70 | ... 71 | ``` 72 | 73 | 74 | ## Thanks 75 | - [schl3ck](https://github.com/schl3ck) for Scriptable types definition: [ios-scriptable-types](https://github.com/schl3ck/ios-scriptable-types) 76 | -------------------------------------------------------------------------------- /modules/LineChart.js: -------------------------------------------------------------------------------- 1 | function normalize(items) { 2 | const min = Math.min(...items) 3 | const max = Math.max(...items) 4 | const diff = max - min; 5 | return items.map(i => (i - min) / diff) 6 | } 7 | 8 | /** 9 | * 10 | * @param {{ data, width, height, chartHeight, spacing }} options chart options 11 | * @returns DrawContext 12 | */ 13 | function drawChart({ data, width, height, chartHeight, spacing }) { 14 | const items = data.map(i => i[0]) 15 | const nitems = normalize(items) 16 | console.log(nitems) 17 | const c = new DrawContext() 18 | c.size = new Size(width, height) 19 | const p = new Path() 20 | p.addLines([ 21 | new Point(0, height), 22 | new Point(0, height) 23 | ]) 24 | // p.addQuadCurve(new Point(150, 150), new Point(0, 150)) 25 | // p.addQuadCurve(new Point(300, 300), new Point(300, 150)) 26 | const step = width / (nitems.length - 1) 27 | const halfStep = step / 2; 28 | 29 | const getPoint = (arr, index) => { 30 | const i = arr[index] 31 | const yy = i * chartHeight 32 | const y = (height - spacing) - yy 33 | return new Point(index * step, y) 34 | } 35 | 36 | const getCurvePoint = (arr, index, mp) => { 37 | const op = getPoint(arr, index + mp); 38 | const p = getPoint(arr, index) 39 | if (mp == -1) { 40 | p.x += halfStep 41 | return p 42 | } else if (mp == 1) { 43 | op.x -= halfStep 44 | return op 45 | } 46 | return p 47 | } 48 | 49 | p.addLine(new Point(0, height)) 50 | 51 | nitems.forEach((i, index, arr) => { 52 | p.addLine( 53 | getPoint(arr, index), 54 | getCurvePoint(arr, index, 1), 55 | getCurvePoint(arr, index, -1) 56 | ) 57 | }) 58 | p.addLine(new Point(width, height)) 59 | p.closeSubpath() 60 | c.addPath(p) 61 | c.setStrokeColor(Color.red()) 62 | //c.setLineWidth(5) 63 | // c.strokePath() 64 | c.setFillColor(Color.purple()) 65 | c.fillPath() 66 | return c; 67 | } 68 | 69 | 70 | module.exports.drawChart = drawChart; 71 | -------------------------------------------------------------------------------- /parse-docs.js: -------------------------------------------------------------------------------- 1 | let methods = [...document.querySelectorAll('.md-clipboard+code')].map(i => { 2 | let row = '\t' + i.textContent.trim(); 3 | if (row.match(/\)$/)) { 4 | row = row.replace(/\)$/, '): void;'); 5 | } else { 6 | row = row + ";"; 7 | } 8 | return row; 9 | }).join('\n'); 10 | let title = document.querySelector('h1').textContent.trim().replace('¶', ''); 11 | copy(`class ${title} \{\n${methods}\}`) -------------------------------------------------------------------------------- /scriptable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | function show_commands() { 5 | echo "Available commands: '$1'" 6 | printf '\r\n' 7 | printf ' ' 8 | printf '%s\n ' "${commands[@]}" 9 | printf '\r\n' 10 | } 11 | 12 | 13 | function upgradeTypes() { 14 | curl https://raw.githubusercontent.com/schl3ck/ios-scriptable-types/master/dist/scriptable.d.ts -o index.d.ts 15 | } 16 | 17 | 18 | function init() { 19 | ln -s ~/Library/Mobile\ Documents/iCloud~dk~simonbs~Scriptable/Documents/ sources 20 | } 21 | 22 | 23 | function import() { 24 | ln ~/Library/Mobile\ Documents/iCloud~dk~simonbs~Scriptable/Documents/"$1.js" "widgets/$1.js" 25 | } 26 | 27 | 28 | function importModule() { 29 | ln ~/Library/Mobile\ Documents/iCloud~dk~simonbs~Scriptable/Documents/modules/"$1.js" "modules/$1.js" 30 | } 31 | 32 | 33 | function list() { 34 | ls ~/Library/Mobile\ Documents/iCloud~dk~simonbs~Scriptable/Documents/ 35 | } 36 | 37 | 38 | commands=(init list import importModule upgradeTypes) 39 | 40 | 41 | if [[ $# -gt 0 ]] && [[ "${commands[@]}" =~ "$1" ]]; then 42 | $1 "${@:2}"; 43 | else 44 | show_commands "$commands" 45 | fi 46 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["ES2015"], 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "forceConsistentCasingInFileNames": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /widgets/Crypto-Line-Chart.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: red; icon-glyph: download; 4 | /// 5 | 6 | const data_ = [[1], [1.9], [4], [2.8], [5], [2.4]]; 7 | const crypto = args.widgetParameter || "btc"; 8 | const req = new Request("https://api.kuna.io/v3/book/" + crypto + "uah"); 9 | const dd = await req.loadJSON(); 10 | const data = dd.slice(0, 30); 11 | data.reverse(); 12 | 13 | const lw = new ListWidget(); 14 | 15 | const chart = importModule("modules/LineChart").drawChart({ 16 | data: data, 17 | width: 300, 18 | height: 300, 19 | chartHeight: 140, 20 | spacing: 50, 21 | }); 22 | lw.backgroundImage = chart.getImage(); 23 | const text = lw.addText(crypto.toUpperCase()); 24 | text.url = "https://github.com"; 25 | text.leftAlignText(); 26 | text.font = Font.boldMonospacedSystemFont(24); 27 | text.textColor = Color.black(); 28 | const dateText = lw.addDate(new Date()); 29 | dateText.applyTimeStyle(); 30 | dateText.font = Font.boldMonospacedSystemFont(8); 31 | dateText.textColor = Color.black(); 32 | dateText.textOpacity = 0.3; 33 | lw.addSpacer(); 34 | Script.setWidget(lw); 35 | lw.presentSmall(); 36 | -------------------------------------------------------------------------------- /widgets/InfoWidget.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: light-brown; icon-glyph: magic; 4 | /// 5 | 6 | const ww = new ListWidget() 7 | ww.setPadding(0,0,0,0); 8 | 9 | 10 | const color = { 11 | shadow: Color.dynamic( 12 | new Color("#000000", 0.01), 13 | new Color("#FFFFFF", 0.04), 14 | ), 15 | cardBackground: Color.dynamic( 16 | new Color("#555555", 0.1), 17 | new Color("#FFFFFF", 0.1), 18 | ), 19 | cardBackgroundSmall: Color.dynamic( 20 | new Color("#555555", 0.1), 21 | new Color("#FFFFFF", 0.12), 22 | ), 23 | caption: Color.dynamic( 24 | new Color("#000000", 0.6), 25 | new Color("#FFFFFF", 0.6), 26 | ), 27 | white: Color.dynamic( 28 | new Color("#000000", 0.8), 29 | Color.white(), 30 | ), 31 | black: Color.dynamic( 32 | Color.white(), 33 | new Color("#000000", 0.8), 34 | ), 35 | } 36 | 37 | function addSFIcon(stack, name, size) { 38 | const sfIcon = SFSymbol.named(name); 39 | sfIcon.applyFont(Font.boldRoundedSystemFont(size)); 40 | const sfImage = stack.addImage(sfIcon.image); 41 | sfImage.tintColor = color.white; 42 | sfImage.imageOpacity = 0.8; 43 | sfImage.imageSize = new Size(size, size); 44 | return sfImage; 45 | } 46 | 47 | 48 | const stackRoot = ww.addStack(); 49 | // stackRoot.borderColor = new Color("#FFFFFF"); 50 | // stackRoot.borderWidth = 6; 51 | stackRoot.setPadding(10, 10, 10, 10); 52 | stackRoot.cornerRadius = 20; 53 | // stackRoot.size = new Size(136, 136); 54 | stackRoot.layoutVertically(); 55 | 56 | const stackTopRoot = stackRoot.addStack(); 57 | stackTopRoot.centerAlignContent(); 58 | const stackTopLeft = stackTopRoot.addStack(); 59 | stackTopRoot.addSpacer(); 60 | // const stackTopCenter = stackTopRoot.addStack(); 61 | // stackTopCenter.backgroundColor = Color.dynamic( 62 | // new Color("#555555", 0.1), 63 | // new Color("#FFFFFF", 0.1), 64 | // ); 65 | // stackTopCenter.size = new Size(2, 38); 66 | // stackTopCenter.cornerRadius = 1; 67 | // stackTopRoot.addSpacer(); 68 | const stackTopRight = stackTopRoot.addStack(); 69 | 70 | stackTopRoot.layoutHorizontally(); 71 | stackTopRoot.cornerRadius = 14; 72 | stackTopRoot.borderWidth = 2; 73 | stackTopRoot.borderColor = color.shadow; 74 | stackTopRoot.backgroundColor = color.cardBackground; 75 | stackTopRoot.setPadding(8, 12, 8, 8); 76 | 77 | stackTopLeft.centerAlignContent(); 78 | stackTopLeft.layoutVertically(); 79 | stackTopLeft.topAlignContent(); 80 | 81 | // stackTopRight.borderColor = Color.red(); 82 | // stackTopRight.borderWidth = 1; 83 | stackTopRight.layoutVertically(); 84 | stackTopRight.bottomAlignContent(); 85 | addSFIcon(stackTopRight, "figure.walk.diamond.fill", 36); 86 | 87 | const titleText = stackTopLeft.addText("128"); 88 | // const titleText = stackTopLeft.addText(`${Device.screenScale()}`); 89 | titleText.font = Font.boldRoundedSystemFont(36); 90 | 91 | const captionText = stackTopLeft.addText("Steps"); 92 | captionText.font = Font.regularRoundedSystemFont(12); 93 | captionText.textColor = color.caption; 94 | 95 | stackRoot.addSpacer(); 96 | 97 | function createFlexibleBlock(root) { 98 | const stack = root.addStack(); 99 | stack.centerAlignContent() 100 | stack.backgroundColor = color.cardBackgroundSmall; 101 | stack.borderWidth = 2; 102 | stack.borderColor = color.shadow; 103 | stack.cornerRadius = 14; 104 | stack.addSpacer(); 105 | const stackCenter = stack.addStack(); 106 | stackCenter.layoutVertically() 107 | stackCenter.addSpacer(); 108 | const stackResult = stackCenter.addStack() 109 | stackCenter.addSpacer(); 110 | stack.addSpacer(); 111 | return stackResult; 112 | } 113 | 114 | const stackBottom = stackRoot.addStack(); 115 | const stackBottomLeft = createFlexibleBlock(stackBottom); 116 | addSFIcon(stackBottomLeft, "video.fill", 28) 117 | stackBottom.addSpacer(); 118 | const stackBottomRight = createFlexibleBlock(stackBottom); 119 | const i = addSFIcon(stackBottomRight, "camera.fill", 28) 120 | stackBottomRight.url = "camera://photo"; 121 | 122 | 123 | // ww.presentSmall(); 124 | ww.presentMedium(); --------------------------------------------------------------------------------