├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── patches └── memfs+3.4.1.patch ├── rollup.config.js └── src ├── assets ├── chameleon.jpg ├── discord-dark.svg ├── discord.svg ├── divriots-dark.svg ├── divriots.svg ├── github.svg ├── meta-image.jpg ├── play-logo.png ├── rss.svg ├── sd-logo.png ├── twitter.svg └── youtube.svg ├── browser ├── FileTree.js ├── FileTreeStyles.css.js ├── card-frame-handler.js ├── codicon.css.js ├── iconDefinitions.js ├── index.js ├── monaco.js └── sdp-nav │ ├── sdp-drawer.js │ ├── sdp-external-links.js │ ├── sdp-logo.js │ ├── sdp-nav.js │ └── src │ ├── SdpDrawer.js │ ├── SdpExternalLinks.js │ ├── SdpLogo.js │ └── SdpNav.js ├── card ├── card.css ├── card.html └── card.js ├── favicon.ico ├── index.html ├── node ├── file-tree-utils.js ├── index.js ├── mkdirRecursive.js └── run-style-dictionary.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | /*.code-workspace 5 | /.history 6 | 7 | ## system files 8 | .DS_Store 9 | 10 | ## npm/yarn 11 | node_modules/ 12 | npm-debug.log 13 | yarn-error.log 14 | 15 | # we use package-lock.json 16 | /yarn.lock 17 | 18 | ## build artifacts 19 | dist/ 20 | 21 | ## temp files 22 | local.log 23 | 24 | ## browserstack 25 | browserstack.err 26 | 27 | debug.log 28 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 126 | at [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 \RIOTS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Style Dictionary Playground 2 | 3 | style dictionary playground logo 4 | 5 |

6 | Brought to you by
7 | 8 | ‹div›RIOTS 9 | 10 | 11 | ‹div›RIOTS 12 | 13 |

14 | 15 | [![Netlify Status](https://api.netlify.com/api/v1/badges/20ef7939-a20f-4e20-86c9-00b39068d090/deploy-status)](https://app.netlify.com/sites/style-dictionary-playground/deploys) 16 | 17 | An interactive playground in the browser for [style-dictionary](https://amzn.github.io/style-dictionary/#/) 18 | 19 | [See it live](https://www.style-dictionary-play.dev/) 20 | 21 | [Read blog post](https://backlight.dev/blog/nodejs-in-browser) 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@divriots/style-dictionary-playground", 3 | "version": "0.1.0", 4 | "description": "Style once, use everywhere. A build system for creating cross-platform styles.", 5 | "license": "MIT", 6 | "author": "divRiots ", 7 | "repository": "https://github.com/divriots/style-dictionary-playground", 8 | "main": "index.js", 9 | "files": [], 10 | "scripts": { 11 | "dev": "npm run build && concurrently \"rollup -c rollup.config.js --watch\" \"wds --watch --root-dir dist\"", 12 | "build": "rimraf dist && rollup -c rollup.config.js", 13 | "postinstall": "patch-package" 14 | }, 15 | "dependencies": { 16 | "@babel/parser": "^7.17.9", 17 | "@lion/core": "^0.18.4", 18 | "@lion/overlays": "^0.26.1", 19 | "@zip.js/zip.js": "^2.3.19", 20 | "browser-style-dictionary": "^3.1.1-browser.2", 21 | "buffer": "^6.0.3", 22 | "glob": "^7.2.0", 23 | "lit": "^2.0.0", 24 | "memfs": "^3.4.1", 25 | "monaco-editor": "^0.28.1", 26 | "pako": "^2.0.4", 27 | "prettier": "^2.6.2", 28 | "process": "^0.11.10", 29 | "stream": "^0.0.2", 30 | "uuid": "^8.3.2", 31 | "wasm-flate": "^1.0.2-browser" 32 | }, 33 | "devDependencies": { 34 | "@rollup/plugin-commonjs": "^21.0.1", 35 | "@rollup/plugin-inject": "^4.0.3", 36 | "@rollup/plugin-json": "^4.1.0", 37 | "@rollup/plugin-node-resolve": "^11.2.1", 38 | "@rollup/plugin-replace": "^3.0.0", 39 | "@web/dev-server": "^0.1.23", 40 | "concurrently": "^6.2.2", 41 | "css-loader": "^6.3.0", 42 | "file-loader": "^6.2.0", 43 | "patch-package": "^6.4.7", 44 | "rimraf": "^3.0.2", 45 | "rollup": "^2.60.1", 46 | "rollup-plugin-copy": "^3.4.0", 47 | "rollup-plugin-visualizer": "^5.5.2", 48 | "style-loader": "^3.3.0", 49 | "watchify": "^4.0.0", 50 | "yargs": "^17.2.1" 51 | }, 52 | "keywords": [ 53 | "style dictionary", 54 | "style", 55 | "dictionary", 56 | "amazon", 57 | "css", 58 | "design", 59 | "properties", 60 | "tokens", 61 | "sass", 62 | "scss", 63 | "iOS", 64 | "Android", 65 | "react", 66 | "react native", 67 | "style guide" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /patches/memfs+3.4.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/memfs/lib/process.js b/node_modules/memfs/lib/process.js 2 | index 0b4cc12..4dc265a 100644 3 | --- a/node_modules/memfs/lib/process.js 4 | +++ b/node_modules/memfs/lib/process.js 5 | @@ -14,7 +14,7 @@ exports.createProcess = void 0; 6 | */ 7 | var maybeReturnProcess = function () { 8 | if (typeof process !== 'undefined') { 9 | - return process; 10 | + return undefined; 11 | } 12 | try { 13 | return require('process'); 14 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import copy from "rollup-plugin-copy"; 2 | import json from "@rollup/plugin-json"; 3 | import resolve from "@rollup/plugin-node-resolve"; 4 | import commonjs from "@rollup/plugin-commonjs"; 5 | import inject from "@rollup/plugin-inject"; 6 | import * as path from "path"; 7 | 8 | const filesToCopy = [ 9 | path.resolve("src", "index.html"), 10 | path.resolve("src", "favicon.ico"), 11 | path.resolve("src", "style.css"), 12 | path.resolve("src", "assets"), 13 | path.resolve("src", "card"), 14 | ]; 15 | 16 | const EMPTY_MODULE_ID = "$empty$"; 17 | const EMPTY_MODULE = `export default {}`; 18 | 19 | const BROWSERIFY_ALIASES = { 20 | assert: "assert", 21 | events: "events", 22 | fs: "memfs", 23 | module: EMPTY_MODULE_ID, 24 | path: "path-browserify", 25 | process: "process", 26 | util: "util", 27 | }; 28 | 29 | const nodeResolve = resolve({ 30 | preferBuiltins: false, 31 | mainFields: ["module", "jsnext:main", "browser"], 32 | }); 33 | 34 | const plugins = [ 35 | commonjs(), 36 | { 37 | name: "browserify", 38 | resolveId(source, importer) { 39 | if (source in BROWSERIFY_ALIASES) { 40 | if (BROWSERIFY_ALIASES[source] === EMPTY_MODULE_ID) 41 | return EMPTY_MODULE_ID; 42 | return nodeResolve.resolveId(BROWSERIFY_ALIASES[source], undefined); 43 | } 44 | if (source === EMPTY_MODULE_ID) return EMPTY_MODULE_ID; 45 | }, 46 | load(id) { 47 | if (id === EMPTY_MODULE_ID) return EMPTY_MODULE; 48 | }, 49 | }, 50 | inject({ 51 | process: "process", 52 | }), 53 | nodeResolve, 54 | json(), 55 | { 56 | name: "watch-external", 57 | buildStart() { 58 | filesToCopy.forEach((file) => this.addWatchFile(file)); 59 | }, 60 | }, 61 | copy({ 62 | targets: [...filesToCopy.map((file) => ({ src: file, dest: "dist" }))], 63 | }), 64 | { 65 | name: "remove-glob-weirdness", 66 | renderChunk(code) { 67 | // No clue why `glob_1.Glob;` ends up in bundle... useless line of code that actually creates fatal error >:( 68 | return code.replace(/glob_1\.Glob;/g, ""); 69 | }, 70 | }, 71 | ]; 72 | 73 | export default [ 74 | { 75 | input: "src/node/index.js", 76 | output: { 77 | format: "es", 78 | inlineDynamicImports: true, 79 | file: "dist/index.js", 80 | globals: { 81 | lodash: "_", 82 | }, 83 | }, 84 | plugins, 85 | }, 86 | ]; 87 | -------------------------------------------------------------------------------- /src/assets/chameleon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divriots/style-dictionary-playground/50459309c60f519440d1b772758f1e6f729e522d/src/assets/chameleon.jpg -------------------------------------------------------------------------------- /src/assets/discord-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/divriots-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 25 | 30 | 31 | 32 | 33 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /src/assets/divriots.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 25 | 30 | 31 | 32 | 33 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /src/assets/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/meta-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divriots/style-dictionary-playground/50459309c60f519440d1b772758f1e6f729e522d/src/assets/meta-image.jpg -------------------------------------------------------------------------------- /src/assets/play-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divriots/style-dictionary-playground/50459309c60f519440d1b772758f1e6f729e522d/src/assets/play-logo.png -------------------------------------------------------------------------------- /src/assets/rss.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/sd-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divriots/style-dictionary-playground/50459309c60f519440d1b772758f1e6f729e522d/src/assets/sd-logo.png -------------------------------------------------------------------------------- /src/assets/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/browser/FileTree.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from "lit"; 2 | import * as zip from "@zip.js/zip.js"; 3 | import iconDefinitions from "./iconDefinitions.js"; 4 | import { ensureMonacoIsLoaded, editor } from "./monaco.js"; 5 | import runStyleDictionary from "../node/run-style-dictionary.js"; 6 | import { 7 | clearAll, 8 | switchToFile, 9 | saveCurrentFile, 10 | createFile, 11 | createFolder, 12 | removeFile, 13 | editFileName, 14 | getAllFiles, 15 | } from "../node/file-tree-utils.js"; 16 | import styles from "./FileTreeStyles.css.js"; 17 | 18 | class FileTree extends LitElement { 19 | static get properties() { 20 | return { 21 | inputFiles: { attribute: false }, 22 | outputFiles: { attribute: false }, 23 | }; 24 | } 25 | 26 | static get styles() { 27 | return [styles]; 28 | } 29 | 30 | get folderButtons() { 31 | return Array.from(this.shadowRoot.querySelectorAll(".folder-row") || []); 32 | } 33 | 34 | /** Get all DOM nodes associated with files */ 35 | get fileButtons() { 36 | return Array.from(this.shadowRoot.querySelectorAll(".file") || []); 37 | } 38 | 39 | /** Get DOM node of the file that is checked */ 40 | get checkedFileBtn() { 41 | return this.fileButtons.find((btn) => btn.hasAttribute("checked")); 42 | } 43 | 44 | /* Get the full file path of the checked file */ 45 | get checkedFile() { 46 | return this.checkedFileBtn?.getAttribute("full-path"); 47 | } 48 | 49 | get checkedFolderEl() { 50 | return Array.from(this.shadowRoot.querySelectorAll(".folder-row")).find( 51 | (folder) => folder.hasAttribute("checked") 52 | ); 53 | } 54 | 55 | get checkedFolder() { 56 | return this.checkedFolderEl?.getAttribute("full-path"); 57 | } 58 | 59 | /* Get DOM node of the file that is unsaved, can only be one for now because we auto-save onFocusChange */ 60 | get unsavedFileBtn() { 61 | return this.fileButtons.find((btn) => btn.hasAttribute("unsaved")); 62 | } 63 | 64 | /* Get the full file path of the unsaved file */ 65 | get unsavedFile() { 66 | return this.unsavedFileBtn.getAttribute("full-path"); 67 | } 68 | 69 | sortFiles(files) { 70 | return files.sort((a, b) => { 71 | const slashesA = a.split("/").length; 72 | const slashesB = b.split("/").length; 73 | if (slashesA < slashesB) { 74 | return 1; 75 | } else if (slashesB < slashesA) { 76 | return -1; 77 | } 78 | return a.localeCompare(b); 79 | }); 80 | } 81 | 82 | /** 83 | * [ 84 | * 'foo.js', 85 | * 'foo/bar/qux.json', 86 | * 'foo/qux/', 87 | * ] 88 | * vvvvvv 89 | * { 90 | * 'foo.js': {}, 91 | * foo: { 92 | * bar: { 93 | * qux.json: 'file' 94 | * }, 95 | * qux: {} 96 | * } 97 | * } 98 | */ 99 | filesAsTree(files) { 100 | const tree = {}; 101 | for (const file of this.sortFiles(files)) { 102 | const parts = file.split("/"); 103 | let depthTree = tree; 104 | for (let i = 0; i < parts.length; i++) { 105 | const part = parts[i]; 106 | 107 | if (i === parts.length - 1) { 108 | if (parts[i] === "") { 109 | continue; 110 | } else { 111 | if (!depthTree[part]) { 112 | depthTree[part] = ""; 113 | } 114 | depthTree = depthTree[part]; 115 | continue; 116 | } 117 | } 118 | 119 | if (!depthTree[part]) { 120 | depthTree[part] = {}; 121 | } 122 | depthTree = depthTree[part]; 123 | } 124 | } 125 | return tree; 126 | } 127 | 128 | /** document.activeElement but incorporating shadow boundaries */ 129 | getDeepActiveElement() { 130 | let host = document.activeElement || document.body; 131 | while (host && host.shadowRoot && host.shadowRoot.activeElement) { 132 | host = host.shadowRoot.activeElement; 133 | } 134 | return host; 135 | } 136 | 137 | constructor() { 138 | super(); 139 | this.inputFiles = []; 140 | this.outputFiles = []; 141 | this.focusInRoot = true; 142 | this.lastSelectedElement; 143 | } 144 | 145 | connectedCallback() { 146 | super.connectedCallback(); 147 | this.addEventListener("click", () => { 148 | if (!this.shadowRoot.contains(this.getDeepActiveElement())) { 149 | this.focusInRoot = true; 150 | this.uncheckFolders(); 151 | } else { 152 | this.focusInRoot = false; 153 | } 154 | }); 155 | } 156 | 157 | updated(changedProperties) { 158 | super.updated(changedProperties); 159 | if ( 160 | changedProperties.has("outputFiles") && 161 | !this.checkedFileBtn && 162 | this.outputFiles.length > 0 163 | ) { 164 | const firstFilePath = this.inputFiles.filter( 165 | (file) => !file.endsWith("/") 166 | )[0]; 167 | const firstFileRow = this.shadowRoot.querySelector( 168 | `[full-path='${firstFilePath}']` 169 | ); 170 | firstFileRow.click(); 171 | } 172 | } 173 | 174 | render() { 175 | return html` 176 |
177 |
178 | 183 | 188 | 193 | 198 | 203 |
204 |
205 |
206 |
207 |
208 | ${this.asDetails(this.filesAsTree(this.inputFiles))} 209 |
210 |
211 |

Output files:

212 | ${this.asDetails(this.filesAsTree(this.outputFiles))} 213 |
214 |
215 | 220 | 225 |
226 |
227 | `; 228 | } 229 | 230 | editorLayout() { 231 | // Need to wait for monaco to catch up to the fact that container size changed (potentially) 232 | // TODO: check if there's a lifecycle hook that we can use instead.. 233 | setTimeout(async () => { 234 | await ensureMonacoIsLoaded(); 235 | editor.layout({}); 236 | editor.layout(); 237 | }, 10); 238 | } 239 | 240 | getLogoFromFileName(filename) { 241 | const spl = filename.split("."); 242 | const ext = spl[spl.length - 1]; 243 | const icon = 244 | iconDefinitions.icons.find((def) => def.fileExtensions?.includes(ext)) 245 | ?.name || iconDefinitions.defaultIcon.name; 246 | return html`file icon`; 250 | } 251 | 252 | asDetails(tree, memo = "") { 253 | return html` 254 | ${Object.entries(tree).map(([k, v]) => { 255 | return v === "" 256 | ? html` 257 |
264 | ${this.getLogoFromFileName(k)} 265 | ${k} 266 |
267 | ` 268 | : html` 269 |
270 | 276 | ${k} 277 | 278 | ${this.asDetails(v, `${memo}${k}/`)} 279 |
280 | `; 281 | })} 282 | `; 283 | } 284 | 285 | play() { 286 | runStyleDictionary(); 287 | } 288 | 289 | uncheckFolders() { 290 | const allFolders = Array.from( 291 | this.shadowRoot.querySelectorAll(".folder-row") 292 | ); 293 | allFolders.forEach((folder) => { 294 | folder.removeAttribute("checked"); 295 | }); 296 | } 297 | 298 | openParentFolders() { 299 | // open parent folders 300 | if (this.checkedFile) { 301 | const parts = this.checkedFile.split("/"); 302 | let path = ""; 303 | parts.forEach((part) => { 304 | path += part; 305 | const el = this.shadowRoot.querySelector(`[full-path="${path}"]`); 306 | if (el) { 307 | el.click(); 308 | } 309 | path += "/"; 310 | }, ""); 311 | } 312 | } 313 | 314 | rowClick(ev) { 315 | let { target, key } = ev; 316 | 317 | // ignore this one, as it's just the file edit "apply" action 318 | if ( 319 | target.classList.contains("edit-file-input") || 320 | (key !== undefined && key !== "F2" && key !== "Enter" && key !== " ") 321 | ) { 322 | return; 323 | } 324 | 325 | let row = target; 326 | if (!target.classList.contains(".row")) { 327 | row = target.closest(".row"); 328 | } 329 | this.lastSelectedElement = row; 330 | if (this.lastSelectedElement.classList.contains("file")) { 331 | this.switchToFile(this.lastSelectedElement.getAttribute("full-path")); 332 | } else if (this.lastSelectedElement.classList.contains("folder-row")) { 333 | this.clickFolder(ev); 334 | } 335 | 336 | const toggleDetails = () => { 337 | const details = ev.target.closest("details"); 338 | if (details && row.classList.contains("folder-row")) { 339 | details.hasAttribute("open") 340 | ? details.removeAttribute("open") 341 | : details.setAttribute("open", ""); 342 | } 343 | }; 344 | 345 | switch (key) { 346 | case "F2": 347 | this.editFileName(); 348 | break; 349 | case " ": 350 | // prevent accidental scrolling 351 | ev.preventDefault(); 352 | toggleDetails(); 353 | break; 354 | } 355 | } 356 | 357 | clickFolder(ev) { 358 | let { target } = ev; 359 | target = target.classList.contains("folder") 360 | ? target.parentElement 361 | : target; 362 | 363 | this.uncheckFolders(); 364 | target.setAttribute("checked", ""); 365 | } 366 | 367 | newFileOrFolder(type) { 368 | const parentFolder = this.focusInRoot 369 | ? this.shadowRoot.getElementById("file-list") 370 | : this.checkedFolderEl?.parentElement; 371 | 372 | const currentFolderText = this.focusInRoot 373 | ? "" 374 | : this.checkedFolderEl.getAttribute("full-path") || ""; 375 | 376 | const input = document.createElement("input"); 377 | input.classList.add("new-file-input"); 378 | if (parentFolder.id === "file-list") { 379 | const outputFiles = parentFolder.querySelector(".output-files"); 380 | outputFiles.insertAdjacentElement("beforebegin", input); 381 | } else { 382 | parentFolder.appendChild(input); 383 | } 384 | 385 | input.closest("details")?.setAttribute("open", ""); 386 | input.addEventListener("blur", (ev) => { 387 | if (ev.target.isConnected) { 388 | this.addFileOrFolder( 389 | ev.target.value, 390 | currentFolderText, 391 | ev.target, 392 | type 393 | ); 394 | } 395 | }); 396 | input.addEventListener("keydown", (ev) => { 397 | if (ev.key === "Enter") { 398 | this.addFileOrFolder( 399 | ev.target.value, 400 | currentFolderText, 401 | ev.target, 402 | type 403 | ); 404 | } 405 | }); 406 | input.focus(); 407 | } 408 | 409 | async addFileOrFolder(filename, folder, input, type) { 410 | try { 411 | input.remove(); 412 | if (filename === "") { 413 | return; 414 | } 415 | const fullPath = `${folder ? `${folder}/` : ""}${filename}`; 416 | const fullPathNormalized = `${fullPath}${type === "folder" ? "/" : ""}`; 417 | this.inputFiles = [...this.inputFiles, fullPathNormalized]; 418 | if (type === "file") { 419 | createFile(fullPathNormalized); 420 | } else if (type === "folder") { 421 | createFolder(fullPathNormalized); 422 | } 423 | await this.updateComplete; 424 | const curr = [...this.folderButtons, ...this.fileButtons].find((btn) => { 425 | return btn.getAttribute("full-path") === fullPath; 426 | }); 427 | 428 | if (curr) { 429 | curr.click(); 430 | curr.setAttribute("checked", true); 431 | this.focusInRoot = false; 432 | } 433 | } catch (e) {} 434 | } 435 | 436 | async editFileName() { 437 | const lastSelectedFilePath = 438 | this.lastSelectedElement.getAttribute("full-path"); 439 | const lastSelectedFileName = this.lastSelectedElement.innerText; 440 | const spanChild = Array.from(this.lastSelectedElement.children).find( 441 | (child) => child.tagName === "SPAN" 442 | ); 443 | const isFolder = spanChild.classList.contains("folder"); 444 | 445 | if (spanChild) { 446 | const inputEl = document.createElement("input"); 447 | inputEl.value = lastSelectedFileName; 448 | inputEl.classList.add("edit-file-input"); 449 | inputEl.setAttribute("size", "10"); 450 | inputEl.setAttribute("aria-label", "Change file name"); 451 | 452 | const applyEdit = (ev) => { 453 | const { value } = ev.target; 454 | this._editFileName(lastSelectedFilePath, value, isFolder); 455 | const newSpanEl = document.createElement("span"); 456 | newSpanEl.innerText = value; 457 | if (isFolder) { 458 | newSpanEl.classList.add("folder"); 459 | } 460 | ev.target.insertAdjacentElement("beforebegin", newSpanEl); 461 | ev.target.isAboutToBeRemoved = true; 462 | ev.target.remove(); 463 | this.lastSelectedElement.focus(); 464 | }; 465 | inputEl.addEventListener("blur", (ev) => { 466 | if (!ev.target.isAboutToBeRemoved) { 467 | applyEdit(ev); 468 | } 469 | }); 470 | inputEl.addEventListener("keydown", (ev) => { 471 | if (ev.key === "Enter") { 472 | applyEdit(ev); 473 | } 474 | }); 475 | spanChild.replaceWith(inputEl); 476 | inputEl.focus(); 477 | } 478 | } 479 | 480 | async _editFileName(filePath, newName, isFolder = false) { 481 | await editFileName(filePath, newName, isFolder); 482 | } 483 | 484 | async removeFileOrFolder() { 485 | const lastSelectedFile = this.lastSelectedElement.getAttribute("full-path"); 486 | const openedDetails = Array.from( 487 | this.shadowRoot.querySelectorAll("details[open]") 488 | ); 489 | await removeFile( 490 | `${lastSelectedFile}${ 491 | this.lastSelectedElement.classList.contains("folder-row") ? "/" : "" 492 | }` 493 | ); 494 | 495 | // Reopen the previously opened folders after render 496 | await this.updateComplete; 497 | openedDetails.forEach((el) => { 498 | el.setAttribute("open", ""); 499 | }); 500 | } 501 | 502 | async switchToFile(indexOrName) { 503 | await this.updateComplete; 504 | if (this.unsavedFileBtn) { 505 | saveCurrentFile(); 506 | } 507 | const filename = this.switchToFileInTree(indexOrName); 508 | switchToFile(filename); 509 | } 510 | 511 | switchToFileInTree(indexOrName) { 512 | if (this.checkedFileBtn) { 513 | this.checkedFileBtn.removeAttribute("checked"); 514 | this.uncheckFolders(); 515 | } 516 | 517 | let filename; 518 | let btn; 519 | if (typeof indexOrName === "number" && this.fileButtons[indexOrName]) { 520 | filename = this.fileButtons[indexOrName].getAttribute("full-path"); 521 | btn = this.fileButtons[indexOrName]; 522 | } else if (typeof indexOrName === "string") { 523 | filename = indexOrName; 524 | btn = this.fileButtons.find( 525 | (btn) => 526 | btn.getAttribute("full-path") === indexOrName.replace(/^\//, "") 527 | ); 528 | } 529 | if (btn) { 530 | btn.setAttribute("checked", ""); 531 | const parentFolder = btn.parentElement.firstElementChild; 532 | if (parentFolder && parentFolder.classList.contains("folder-row")) { 533 | parentFolder.setAttribute("checked", ""); 534 | } 535 | } 536 | return filename; 537 | } 538 | 539 | animateCue() { 540 | const cueEl = this.shadowRoot?.querySelector(".loading-cue-overlay"); 541 | if (cueEl) { 542 | cueEl.classList.remove("loading-cue-overlay--slide"); 543 | // This triggers browser to stop batching changes because it has to evaluate something. 544 | // eslint-disable-next-line no-void 545 | void this.offsetWidth; 546 | // So that when we arrive here, the browser sees this adding as an actual 'change' 547 | // and this means the animation gets refired. 548 | cueEl.classList.add("loading-cue-overlay--slide"); 549 | } 550 | } 551 | 552 | async downloadAsZIP() { 553 | const zipWriter = new zip.ZipWriter(new zip.BlobWriter("application/zip")); 554 | 555 | // Add all files to zip 556 | const allFiles = await getAllFiles(); 557 | await Promise.all( 558 | Object.entries(allFiles).map(([key, value]) => 559 | zipWriter.add(key, new zip.TextReader(value)) 560 | ) 561 | ); 562 | 563 | // Close zip and make into URL 564 | const dataURI = await zipWriter.close(); 565 | const url = URL.createObjectURL(dataURI); 566 | 567 | // Auto-download the ZIP through anchor 568 | const anchor = document.createElement("a"); 569 | anchor.href = url; 570 | const today = new Date(); 571 | anchor.download = `sd-export-${today.getFullYear()}-${today.getMonth()}-${( 572 | "0" + today.getDate() 573 | ).slice(-2)}.zip`; 574 | document.body.appendChild(anchor); 575 | anchor.click(); 576 | document.body.removeChild(anchor); 577 | } 578 | } 579 | customElements.define("file-tree", FileTree); 580 | -------------------------------------------------------------------------------- /src/browser/FileTreeStyles.css.js: -------------------------------------------------------------------------------- 1 | import { css } from "lit"; 2 | 3 | export default css` 4 | :host { 5 | display: flex; 6 | flex-direction: column; 7 | align-items: stretch; 8 | background-color: #171717; 9 | position: relative; 10 | } 11 | 12 | .codicon[class*="codicon-"] { 13 | font: normal normal normal 16px/1 codicon; 14 | background-color: transparent; 15 | border: none; 16 | cursor: pointer; 17 | } 18 | 19 | .codicon-clear-all:before { 20 | content: "\\eabf"; 21 | } 22 | .codicon-cloud-download:before { 23 | content: "\\eac2"; 24 | } 25 | .codicon-edit:before { 26 | content: "\\ea73"; 27 | } 28 | .codicon-new-file:before { 29 | content: "\\ea7f"; 30 | } 31 | .codicon-new-folder:before { 32 | content: "\\ea80"; 33 | } 34 | .codicon-play:before { 35 | content: "\\eb2c"; 36 | } 37 | .codicon-trash:before { 38 | content: "\\ea81"; 39 | } 40 | 41 | #file-list { 42 | display: flex; 43 | flex-direction: column; 44 | height: 100%; 45 | width: 200px; 46 | color: white; 47 | overflow-y: auto; 48 | } 49 | 50 | .file, 51 | .folder { 52 | display: inline-block; 53 | width: max-content; 54 | } 55 | 56 | .folder { 57 | padding: 0.25em 0em; 58 | } 59 | 60 | .file { 61 | display: flex; 62 | position: relative; 63 | align-items: center; 64 | margin-left: 1em; 65 | } 66 | 67 | .file > span { 68 | white-space: nowrap; 69 | } 70 | 71 | #file-list > details { 72 | margin-bottom: 0.5em; 73 | } 74 | 75 | details { 76 | padding-left: 0.75em; 77 | } 78 | 79 | summary { 80 | padding-left: 0.25em; 81 | } 82 | 83 | .row { 84 | cursor: pointer; 85 | padding-right: 1.25rem; 86 | } 87 | 88 | img { 89 | width: 18px; 90 | padding: 5px; 91 | display: block; 92 | } 93 | 94 | .file::after { 95 | content: "●"; 96 | position: absolute; 97 | color: transparent; 98 | right: 0.375rem; 99 | } 100 | 101 | .row:hover { 102 | background-color: #292929; 103 | } 104 | 105 | .file[checked] { 106 | background-color: #f8c307; 107 | background-color: #524310; 108 | } 109 | 110 | .file[unsaved]::after { 111 | color: white; 112 | } 113 | 114 | .folder-row[checked] { 115 | background-color: #292929; 116 | } 117 | 118 | .new-file-input { 119 | margin-left: 1.5em; 120 | width: calc(100% - 3em); 121 | margin-right: 2em; 122 | } 123 | 124 | .new { 125 | display: flex; 126 | justify-content: flex-end; 127 | position: sticky; 128 | top: 0; 129 | margin-bottom: 2px; 130 | background-color: #171717; 131 | border-top-left-radius: var(--border-radius-editor); 132 | } 133 | 134 | .new > .codicon, 135 | .clear > .codicon { 136 | padding: 0.5em; 137 | background-color: transparent; 138 | color: white; 139 | border: none; 140 | cursor: pointer; 141 | } 142 | 143 | .new > .codicon:hover, 144 | .clear > .codicon:hover { 145 | background-color: rgba(255, 255, 255, 0.1); 146 | } 147 | 148 | .codicon-play { 149 | flex-grow: 1; 150 | } 151 | 152 | .loading-cue { 153 | position: relative; 154 | height: 3px; 155 | margin-bottom: 6px; 156 | background-color: #f8c30730; 157 | overflow: hidden; 158 | } 159 | 160 | .loading-cue-overlay { 161 | position: absolute; 162 | height: 3px; 163 | width: 50px; 164 | left: -50px; 165 | background-color: #f8c307; 166 | } 167 | 168 | .loading-cue-overlay--slide { 169 | animation: slidethrough 500ms ease-in; 170 | } 171 | 172 | @keyframes slidethrough { 173 | from { 174 | left: -50px; 175 | width: 50px; 176 | } 177 | to { 178 | left: 100%; 179 | width: 500px; 180 | } 181 | } 182 | 183 | .input-files { 184 | flex-grow: 1; 185 | overflow-x: auto; 186 | } 187 | 188 | .output-files { 189 | display: flex; 190 | flex-direction: column; 191 | justify-content: flex-end; 192 | } 193 | 194 | .output-files > p { 195 | padding-top: 0.5rem; 196 | margin: 1.5rem 0 0.5rem 0; 197 | text-align: center; 198 | border-top: 1px solid white; 199 | } 200 | 201 | .clear { 202 | width: 100%; 203 | position: sticky; 204 | bottom: 0; 205 | display: flex; 206 | justify-content: center; 207 | overflow: hidden; 208 | border-bottom-left-radius: var(--border-radius-editor); 209 | } 210 | 211 | .clear > .codicon { 212 | width: 100%; 213 | background-color: #171717; 214 | } 215 | `; 216 | -------------------------------------------------------------------------------- /src/browser/card-frame-handler.js: -------------------------------------------------------------------------------- 1 | const cardFrame = document.getElementById("card-frame"); 2 | const viewportBtn = document.getElementById("viewport-btn"); 3 | const popoutBtn = document.getElementById("popout-btn"); 4 | const framePopout = document.getElementById("frame-popout"); 5 | 6 | cardFrame.style.width = "min(80vw, 700px)"; 7 | 8 | viewportBtn.addEventListener("click", (ev) => { 9 | const { target } = ev; 10 | if (target.innerText === "View Mobile") { 11 | target.innerText = "View Desktop"; 12 | cardFrame.style.width = "min(80vw, 400px)"; 13 | } else { 14 | target.innerText = "View Mobile"; 15 | cardFrame.style.width = "min(80vw, 700px)"; 16 | } 17 | }); 18 | 19 | popoutBtn.addEventListener("click", (ev) => { 20 | const { target } = ev; 21 | if (target.innerText === "Pop-out") { 22 | framePopout.style.position = "fixed"; 23 | framePopout.style.transform = "translateY(-50%)"; 24 | target.innerText = "Pop-in"; 25 | } else { 26 | framePopout.style.position = "relative"; 27 | framePopout.style.transform = "none"; 28 | target.innerText = "Pop-out"; 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /src/browser/codicon.css.js: -------------------------------------------------------------------------------- 1 | import { css } from "lit"; 2 | 3 | export default css` 4 | .codicon[class*="codicon-"] { 5 | font: normal normal normal 16px/1 codicon; 6 | display: block; 7 | text-decoration: none; 8 | text-rendering: auto; 9 | text-align: center; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | user-select: none; 13 | -webkit-user-select: none; 14 | -ms-user-select: none; 15 | } 16 | 17 | .codicon-add:before { 18 | content: "\\ea60"; 19 | } 20 | .codicon-plus:before { 21 | content: "\\ea60"; 22 | } 23 | .codicon-gist-new:before { 24 | content: "\\ea60"; 25 | } 26 | .codicon-repo-create:before { 27 | content: "\\ea60"; 28 | } 29 | .codicon-lightbulb:before { 30 | content: "\\ea61"; 31 | } 32 | .codicon-light-bulb:before { 33 | content: "\\ea61"; 34 | } 35 | .codicon-repo:before { 36 | content: "\\ea62"; 37 | } 38 | .codicon-repo-delete:before { 39 | content: "\\ea62"; 40 | } 41 | .codicon-gist-fork:before { 42 | content: "\\ea63"; 43 | } 44 | .codicon-repo-forked:before { 45 | content: "\\ea63"; 46 | } 47 | .codicon-git-pull-request:before { 48 | content: "\\ea64"; 49 | } 50 | .codicon-git-pull-request-abandoned:before { 51 | content: "\\ea64"; 52 | } 53 | .codicon-record-keys:before { 54 | content: "\\ea65"; 55 | } 56 | .codicon-keyboard:before { 57 | content: "\\ea65"; 58 | } 59 | .codicon-tag:before { 60 | content: "\\ea66"; 61 | } 62 | .codicon-tag-add:before { 63 | content: "\\ea66"; 64 | } 65 | .codicon-tag-remove:before { 66 | content: "\\ea66"; 67 | } 68 | .codicon-person:before { 69 | content: "\\ea67"; 70 | } 71 | .codicon-person-follow:before { 72 | content: "\\ea67"; 73 | } 74 | .codicon-person-outline:before { 75 | content: "\\ea67"; 76 | } 77 | .codicon-person-filled:before { 78 | content: "\\ea67"; 79 | } 80 | .codicon-git-branch:before { 81 | content: "\\ea68"; 82 | } 83 | .codicon-git-branch-create:before { 84 | content: "\\ea68"; 85 | } 86 | .codicon-git-branch-delete:before { 87 | content: "\\ea68"; 88 | } 89 | .codicon-source-control:before { 90 | content: "\\ea68"; 91 | } 92 | .codicon-mirror:before { 93 | content: "\\ea69"; 94 | } 95 | .codicon-mirror-public:before { 96 | content: "\\ea69"; 97 | } 98 | .codicon-star:before { 99 | content: "\\ea6a"; 100 | } 101 | .codicon-star-add:before { 102 | content: "\\ea6a"; 103 | } 104 | .codicon-star-delete:before { 105 | content: "\\ea6a"; 106 | } 107 | .codicon-star-empty:before { 108 | content: "\\ea6a"; 109 | } 110 | .codicon-comment:before { 111 | content: "\\ea6b"; 112 | } 113 | .codicon-comment-add:before { 114 | content: "\\ea6b"; 115 | } 116 | .codicon-alert:before { 117 | content: "\\ea6c"; 118 | } 119 | .codicon-warning:before { 120 | content: "\\ea6c"; 121 | } 122 | .codicon-search:before { 123 | content: "\\ea6d"; 124 | } 125 | .codicon-search-save:before { 126 | content: "\\ea6d"; 127 | } 128 | .codicon-log-out:before { 129 | content: "\\ea6e"; 130 | } 131 | .codicon-sign-out:before { 132 | content: "\\ea6e"; 133 | } 134 | .codicon-log-in:before { 135 | content: "\\ea6f"; 136 | } 137 | .codicon-sign-in:before { 138 | content: "\\ea6f"; 139 | } 140 | .codicon-eye:before { 141 | content: "\\ea70"; 142 | } 143 | .codicon-eye-unwatch:before { 144 | content: "\\ea70"; 145 | } 146 | .codicon-eye-watch:before { 147 | content: "\\ea70"; 148 | } 149 | .codicon-circle-filled:before { 150 | content: "\\ea71"; 151 | } 152 | .codicon-primitive-dot:before { 153 | content: "\\ea71"; 154 | } 155 | .codicon-close-dirty:before { 156 | content: "\\ea71"; 157 | } 158 | .codicon-debug-breakpoint:before { 159 | content: "\\ea71"; 160 | } 161 | .codicon-debug-breakpoint-disabled:before { 162 | content: "\\ea71"; 163 | } 164 | .codicon-debug-hint:before { 165 | content: "\\ea71"; 166 | } 167 | .codicon-primitive-square:before { 168 | content: "\\ea72"; 169 | } 170 | .codicon-edit:before { 171 | content: "\\ea73"; 172 | } 173 | .codicon-pencil:before { 174 | content: "\\ea73"; 175 | } 176 | .codicon-info:before { 177 | content: "\\ea74"; 178 | } 179 | .codicon-issue-opened:before { 180 | content: "\\ea74"; 181 | } 182 | .codicon-gist-private:before { 183 | content: "\\ea75"; 184 | } 185 | .codicon-git-fork-private:before { 186 | content: "\\ea75"; 187 | } 188 | .codicon-lock:before { 189 | content: "\\ea75"; 190 | } 191 | .codicon-mirror-private:before { 192 | content: "\\ea75"; 193 | } 194 | .codicon-close:before { 195 | content: "\\ea76"; 196 | } 197 | .codicon-remove-close:before { 198 | content: "\\ea76"; 199 | } 200 | .codicon-x:before { 201 | content: "\\ea76"; 202 | } 203 | .codicon-repo-sync:before { 204 | content: "\\ea77"; 205 | } 206 | .codicon-sync:before { 207 | content: "\\ea77"; 208 | } 209 | .codicon-clone:before { 210 | content: "\\ea78"; 211 | } 212 | .codicon-desktop-download:before { 213 | content: "\\ea78"; 214 | } 215 | .codicon-beaker:before { 216 | content: "\\ea79"; 217 | } 218 | .codicon-microscope:before { 219 | content: "\\ea79"; 220 | } 221 | .codicon-vm:before { 222 | content: "\\ea7a"; 223 | } 224 | .codicon-device-desktop:before { 225 | content: "\\ea7a"; 226 | } 227 | .codicon-file:before { 228 | content: "\\ea7b"; 229 | } 230 | .codicon-file-text:before { 231 | content: "\\ea7b"; 232 | } 233 | .codicon-more:before { 234 | content: "\\ea7c"; 235 | } 236 | .codicon-ellipsis:before { 237 | content: "\\ea7c"; 238 | } 239 | .codicon-kebab-horizontal:before { 240 | content: "\\ea7c"; 241 | } 242 | .codicon-mail-reply:before { 243 | content: "\\ea7d"; 244 | } 245 | .codicon-reply:before { 246 | content: "\\ea7d"; 247 | } 248 | .codicon-organization:before { 249 | content: "\\ea7e"; 250 | } 251 | .codicon-organization-filled:before { 252 | content: "\\ea7e"; 253 | } 254 | .codicon-organization-outline:before { 255 | content: "\\ea7e"; 256 | } 257 | .codicon-new-file:before { 258 | content: "\\ea7f"; 259 | } 260 | .codicon-file-add:before { 261 | content: "\\ea7f"; 262 | } 263 | .codicon-new-folder:before { 264 | content: "\\ea80"; 265 | } 266 | .codicon-file-directory-create:before { 267 | content: "\\ea80"; 268 | } 269 | .codicon-trash:before { 270 | content: "\\ea81"; 271 | } 272 | .codicon-trashcan:before { 273 | content: "\\ea81"; 274 | } 275 | .codicon-history:before { 276 | content: "\\ea82"; 277 | } 278 | .codicon-clock:before { 279 | content: "\\ea82"; 280 | } 281 | .codicon-folder:before { 282 | content: "\\ea83"; 283 | } 284 | .codicon-file-directory:before { 285 | content: "\\ea83"; 286 | } 287 | .codicon-symbol-folder:before { 288 | content: "\\ea83"; 289 | } 290 | .codicon-logo-github:before { 291 | content: "\\ea84"; 292 | } 293 | .codicon-mark-github:before { 294 | content: "\\ea84"; 295 | } 296 | .codicon-github:before { 297 | content: "\\ea84"; 298 | } 299 | .codicon-terminal:before { 300 | content: "\\ea85"; 301 | } 302 | .codicon-console:before { 303 | content: "\\ea85"; 304 | } 305 | .codicon-repl:before { 306 | content: "\\ea85"; 307 | } 308 | .codicon-zap:before { 309 | content: "\\ea86"; 310 | } 311 | .codicon-symbol-event:before { 312 | content: "\\ea86"; 313 | } 314 | .codicon-error:before { 315 | content: "\\ea87"; 316 | } 317 | .codicon-stop:before { 318 | content: "\\ea87"; 319 | } 320 | .codicon-variable:before { 321 | content: "\\ea88"; 322 | } 323 | .codicon-symbol-variable:before { 324 | content: "\\ea88"; 325 | } 326 | .codicon-array:before { 327 | content: "\\ea8a"; 328 | } 329 | .codicon-symbol-array:before { 330 | content: "\\ea8a"; 331 | } 332 | .codicon-symbol-module:before { 333 | content: "\\ea8b"; 334 | } 335 | .codicon-symbol-package:before { 336 | content: "\\ea8b"; 337 | } 338 | .codicon-symbol-namespace:before { 339 | content: "\\ea8b"; 340 | } 341 | .codicon-symbol-object:before { 342 | content: "\\ea8b"; 343 | } 344 | .codicon-symbol-method:before { 345 | content: "\\ea8c"; 346 | } 347 | .codicon-symbol-function:before { 348 | content: "\\ea8c"; 349 | } 350 | .codicon-symbol-constructor:before { 351 | content: "\\ea8c"; 352 | } 353 | .codicon-symbol-boolean:before { 354 | content: "\\ea8f"; 355 | } 356 | .codicon-symbol-null:before { 357 | content: "\\ea8f"; 358 | } 359 | .codicon-symbol-numeric:before { 360 | content: "\\ea90"; 361 | } 362 | .codicon-symbol-number:before { 363 | content: "\\ea90"; 364 | } 365 | .codicon-symbol-structure:before { 366 | content: "\\ea91"; 367 | } 368 | .codicon-symbol-struct:before { 369 | content: "\\ea91"; 370 | } 371 | .codicon-symbol-parameter:before { 372 | content: "\\ea92"; 373 | } 374 | .codicon-symbol-type-parameter:before { 375 | content: "\\ea92"; 376 | } 377 | .codicon-symbol-key:before { 378 | content: "\\ea93"; 379 | } 380 | .codicon-symbol-text:before { 381 | content: "\\ea93"; 382 | } 383 | .codicon-symbol-reference:before { 384 | content: "\\ea94"; 385 | } 386 | .codicon-go-to-file:before { 387 | content: "\\ea94"; 388 | } 389 | .codicon-symbol-enum:before { 390 | content: "\\ea95"; 391 | } 392 | .codicon-symbol-value:before { 393 | content: "\\ea95"; 394 | } 395 | .codicon-symbol-ruler:before { 396 | content: "\\ea96"; 397 | } 398 | .codicon-symbol-unit:before { 399 | content: "\\ea96"; 400 | } 401 | .codicon-activate-breakpoints:before { 402 | content: "\\ea97"; 403 | } 404 | .codicon-archive:before { 405 | content: "\\ea98"; 406 | } 407 | .codicon-arrow-both:before { 408 | content: "\\ea99"; 409 | } 410 | .codicon-arrow-down:before { 411 | content: "\\ea9a"; 412 | } 413 | .codicon-arrow-left:before { 414 | content: "\\ea9b"; 415 | } 416 | .codicon-arrow-right:before { 417 | content: "\\ea9c"; 418 | } 419 | .codicon-arrow-small-down:before { 420 | content: "\\ea9d"; 421 | } 422 | .codicon-arrow-small-left:before { 423 | content: "\\ea9e"; 424 | } 425 | .codicon-arrow-small-right:before { 426 | content: "\\ea9f"; 427 | } 428 | .codicon-arrow-small-up:before { 429 | content: "\\eaa0"; 430 | } 431 | .codicon-arrow-up:before { 432 | content: "\\eaa1"; 433 | } 434 | .codicon-bell:before { 435 | content: "\\eaa2"; 436 | } 437 | .codicon-bold:before { 438 | content: "\\eaa3"; 439 | } 440 | .codicon-book:before { 441 | content: "\\eaa4"; 442 | } 443 | .codicon-bookmark:before { 444 | content: "\\eaa5"; 445 | } 446 | .codicon-debug-breakpoint-conditional-unverified:before { 447 | content: "\\eaa6"; 448 | } 449 | .codicon-debug-breakpoint-conditional:before { 450 | content: "\\eaa7"; 451 | } 452 | .codicon-debug-breakpoint-conditional-disabled:before { 453 | content: "\\eaa7"; 454 | } 455 | .codicon-debug-breakpoint-data-unverified:before { 456 | content: "\\eaa8"; 457 | } 458 | .codicon-debug-breakpoint-data:before { 459 | content: "\\eaa9"; 460 | } 461 | .codicon-debug-breakpoint-data-disabled:before { 462 | content: "\\eaa9"; 463 | } 464 | .codicon-debug-breakpoint-log-unverified:before { 465 | content: "\\eaaa"; 466 | } 467 | .codicon-debug-breakpoint-log:before { 468 | content: "\\eaab"; 469 | } 470 | .codicon-debug-breakpoint-log-disabled:before { 471 | content: "\\eaab"; 472 | } 473 | .codicon-briefcase:before { 474 | content: "\\eaac"; 475 | } 476 | .codicon-broadcast:before { 477 | content: "\\eaad"; 478 | } 479 | .codicon-browser:before { 480 | content: "\\eaae"; 481 | } 482 | .codicon-bug:before { 483 | content: "\\eaaf"; 484 | } 485 | .codicon-calendar:before { 486 | content: "\\eab0"; 487 | } 488 | .codicon-case-sensitive:before { 489 | content: "\\eab1"; 490 | } 491 | .codicon-check:before { 492 | content: "\\eab2"; 493 | } 494 | .codicon-checklist:before { 495 | content: "\\eab3"; 496 | } 497 | .codicon-chevron-down:before { 498 | content: "\\eab4"; 499 | } 500 | .codicon-chevron-left:before { 501 | content: "\\eab5"; 502 | } 503 | .codicon-chevron-right:before { 504 | content: "\\eab6"; 505 | } 506 | .codicon-chevron-up:before { 507 | content: "\\eab7"; 508 | } 509 | .codicon-chrome-close:before { 510 | content: "\\eab8"; 511 | } 512 | .codicon-chrome-maximize:before { 513 | content: "\\eab9"; 514 | } 515 | .codicon-chrome-minimize:before { 516 | content: "\\eaba"; 517 | } 518 | .codicon-chrome-restore:before { 519 | content: "\\eabb"; 520 | } 521 | .codicon-circle-outline:before { 522 | content: "\\eabc"; 523 | } 524 | .codicon-debug-breakpoint-unverified:before { 525 | content: "\\eabc"; 526 | } 527 | .codicon-circle-slash:before { 528 | content: "\\eabd"; 529 | } 530 | .codicon-circuit-board:before { 531 | content: "\\eabe"; 532 | } 533 | .codicon-clear-all:before { 534 | content: "\\eabf"; 535 | } 536 | .codicon-clippy:before { 537 | content: "\\eac0"; 538 | } 539 | .codicon-close-all:before { 540 | content: "\\eac1"; 541 | } 542 | .codicon-cloud-download:before { 543 | content: "\\eac2"; 544 | } 545 | .codicon-cloud-upload:before { 546 | content: "\\eac3"; 547 | } 548 | .codicon-code:before { 549 | content: "\\eac4"; 550 | } 551 | .codicon-collapse-all:before { 552 | content: "\\eac5"; 553 | } 554 | .codicon-color-mode:before { 555 | content: "\\eac6"; 556 | } 557 | .codicon-comment-discussion:before { 558 | content: "\\eac7"; 559 | } 560 | .codicon-credit-card:before { 561 | content: "\\eac9"; 562 | } 563 | .codicon-dash:before { 564 | content: "\\eacc"; 565 | } 566 | .codicon-dashboard:before { 567 | content: "\\eacd"; 568 | } 569 | .codicon-database:before { 570 | content: "\\eace"; 571 | } 572 | .codicon-debug-continue:before { 573 | content: "\\eacf"; 574 | } 575 | .codicon-debug-disconnect:before { 576 | content: "\\ead0"; 577 | } 578 | .codicon-debug-pause:before { 579 | content: "\\ead1"; 580 | } 581 | .codicon-debug-restart:before { 582 | content: "\\ead2"; 583 | } 584 | .codicon-debug-start:before { 585 | content: "\\ead3"; 586 | } 587 | .codicon-debug-step-into:before { 588 | content: "\\ead4"; 589 | } 590 | .codicon-debug-step-out:before { 591 | content: "\\ead5"; 592 | } 593 | .codicon-debug-step-over:before { 594 | content: "\\ead6"; 595 | } 596 | .codicon-debug-stop:before { 597 | content: "\\ead7"; 598 | } 599 | .codicon-debug:before { 600 | content: "\\ead8"; 601 | } 602 | .codicon-device-camera-video:before { 603 | content: "\\ead9"; 604 | } 605 | .codicon-device-camera:before { 606 | content: "\\eada"; 607 | } 608 | .codicon-device-mobile:before { 609 | content: "\\eadb"; 610 | } 611 | .codicon-diff-added:before { 612 | content: "\\eadc"; 613 | } 614 | .codicon-diff-ignored:before { 615 | content: "\\eadd"; 616 | } 617 | .codicon-diff-modified:before { 618 | content: "\\eade"; 619 | } 620 | .codicon-diff-removed:before { 621 | content: "\\eadf"; 622 | } 623 | .codicon-diff-renamed:before { 624 | content: "\\eae0"; 625 | } 626 | .codicon-diff:before { 627 | content: "\\eae1"; 628 | } 629 | .codicon-discard:before { 630 | content: "\\eae2"; 631 | } 632 | .codicon-editor-layout:before { 633 | content: "\\eae3"; 634 | } 635 | .codicon-empty-window:before { 636 | content: "\\eae4"; 637 | } 638 | .codicon-exclude:before { 639 | content: "\\eae5"; 640 | } 641 | .codicon-extensions:before { 642 | content: "\\eae6"; 643 | } 644 | .codicon-eye-closed:before { 645 | content: "\\eae7"; 646 | } 647 | .codicon-file-binary:before { 648 | content: "\\eae8"; 649 | } 650 | .codicon-file-code:before { 651 | content: "\\eae9"; 652 | } 653 | .codicon-file-media:before { 654 | content: "\\eaea"; 655 | } 656 | .codicon-file-pdf:before { 657 | content: "\\eaeb"; 658 | } 659 | .codicon-file-submodule:before { 660 | content: "\\eaec"; 661 | } 662 | .codicon-file-symlink-directory:before { 663 | content: "\\eaed"; 664 | } 665 | .codicon-file-symlink-file:before { 666 | content: "\\eaee"; 667 | } 668 | .codicon-file-zip:before { 669 | content: "\\eaef"; 670 | } 671 | .codicon-files:before { 672 | content: "\\eaf0"; 673 | } 674 | .codicon-filter:before { 675 | content: "\\eaf1"; 676 | } 677 | .codicon-flame:before { 678 | content: "\\eaf2"; 679 | } 680 | .codicon-fold-down:before { 681 | content: "\\eaf3"; 682 | } 683 | .codicon-fold-up:before { 684 | content: "\\eaf4"; 685 | } 686 | .codicon-fold:before { 687 | content: "\\eaf5"; 688 | } 689 | .codicon-folder-active:before { 690 | content: "\\eaf6"; 691 | } 692 | .codicon-folder-opened:before { 693 | content: "\\eaf7"; 694 | } 695 | .codicon-gear:before { 696 | content: "\\eaf8"; 697 | } 698 | .codicon-gift:before { 699 | content: "\\eaf9"; 700 | } 701 | .codicon-gist-secret:before { 702 | content: "\\eafa"; 703 | } 704 | .codicon-gist:before { 705 | content: "\\eafb"; 706 | } 707 | .codicon-git-commit:before { 708 | content: "\\eafc"; 709 | } 710 | .codicon-git-compare:before { 711 | content: "\\eafd"; 712 | } 713 | .codicon-compare-changes:before { 714 | content: "\\eafd"; 715 | } 716 | .codicon-git-merge:before { 717 | content: "\\eafe"; 718 | } 719 | .codicon-github-action:before { 720 | content: "\\eaff"; 721 | } 722 | .codicon-github-alt:before { 723 | content: "\\eb00"; 724 | } 725 | .codicon-globe:before { 726 | content: "\\eb01"; 727 | } 728 | .codicon-grabber:before { 729 | content: "\\eb02"; 730 | } 731 | .codicon-graph:before { 732 | content: "\\eb03"; 733 | } 734 | .codicon-gripper:before { 735 | content: "\\eb04"; 736 | } 737 | .codicon-heart:before { 738 | content: "\\eb05"; 739 | } 740 | .codicon-home:before { 741 | content: "\\eb06"; 742 | } 743 | .codicon-horizontal-rule:before { 744 | content: "\\eb07"; 745 | } 746 | .codicon-hubot:before { 747 | content: "\\eb08"; 748 | } 749 | .codicon-inbox:before { 750 | content: "\\eb09"; 751 | } 752 | .codicon-issue-reopened:before { 753 | content: "\\eb0b"; 754 | } 755 | .codicon-issues:before { 756 | content: "\\eb0c"; 757 | } 758 | .codicon-italic:before { 759 | content: "\\eb0d"; 760 | } 761 | .codicon-jersey:before { 762 | content: "\\eb0e"; 763 | } 764 | .codicon-json:before { 765 | content: "\\eb0f"; 766 | } 767 | .codicon-kebab-vertical:before { 768 | content: "\\eb10"; 769 | } 770 | .codicon-key:before { 771 | content: "\\eb11"; 772 | } 773 | .codicon-law:before { 774 | content: "\\eb12"; 775 | } 776 | .codicon-lightbulb-autofix:before { 777 | content: "\\eb13"; 778 | } 779 | .codicon-link-external:before { 780 | content: "\\eb14"; 781 | } 782 | .codicon-link:before { 783 | content: "\\eb15"; 784 | } 785 | .codicon-list-ordered:before { 786 | content: "\\eb16"; 787 | } 788 | .codicon-list-unordered:before { 789 | content: "\\eb17"; 790 | } 791 | .codicon-live-share:before { 792 | content: "\\eb18"; 793 | } 794 | .codicon-loading:before { 795 | content: "\\eb19"; 796 | } 797 | .codicon-location:before { 798 | content: "\\eb1a"; 799 | } 800 | .codicon-mail-read:before { 801 | content: "\\eb1b"; 802 | } 803 | .codicon-mail:before { 804 | content: "\\eb1c"; 805 | } 806 | .codicon-markdown:before { 807 | content: "\\eb1d"; 808 | } 809 | .codicon-megaphone:before { 810 | content: "\\eb1e"; 811 | } 812 | .codicon-mention:before { 813 | content: "\\eb1f"; 814 | } 815 | .codicon-milestone:before { 816 | content: "\\eb20"; 817 | } 818 | .codicon-mortar-board:before { 819 | content: "\\eb21"; 820 | } 821 | .codicon-move:before { 822 | content: "\\eb22"; 823 | } 824 | .codicon-multiple-windows:before { 825 | content: "\\eb23"; 826 | } 827 | .codicon-mute:before { 828 | content: "\\eb24"; 829 | } 830 | .codicon-no-newline:before { 831 | content: "\\eb25"; 832 | } 833 | .codicon-note:before { 834 | content: "\\eb26"; 835 | } 836 | .codicon-octoface:before { 837 | content: "\\eb27"; 838 | } 839 | .codicon-open-preview:before { 840 | content: "\\eb28"; 841 | } 842 | .codicon-package:before { 843 | content: "\\eb29"; 844 | } 845 | .codicon-paintcan:before { 846 | content: "\\eb2a"; 847 | } 848 | .codicon-pin:before { 849 | content: "\\eb2b"; 850 | } 851 | .codicon-play:before { 852 | content: "\\eb2c"; 853 | } 854 | .codicon-run:before { 855 | content: "\\eb2c"; 856 | } 857 | .codicon-plug:before { 858 | content: "\\eb2d"; 859 | } 860 | .codicon-preserve-case:before { 861 | content: "\\eb2e"; 862 | } 863 | .codicon-preview:before { 864 | content: "\\eb2f"; 865 | } 866 | .codicon-project:before { 867 | content: "\\eb30"; 868 | } 869 | .codicon-pulse:before { 870 | content: "\\eb31"; 871 | } 872 | .codicon-question:before { 873 | content: "\\eb32"; 874 | } 875 | .codicon-quote:before { 876 | content: "\\eb33"; 877 | } 878 | .codicon-radio-tower:before { 879 | content: "\\eb34"; 880 | } 881 | .codicon-reactions:before { 882 | content: "\\eb35"; 883 | } 884 | .codicon-references:before { 885 | content: "\\eb36"; 886 | } 887 | .codicon-refresh:before { 888 | content: "\\eb37"; 889 | } 890 | .codicon-regex:before { 891 | content: "\\eb38"; 892 | } 893 | .codicon-remote-explorer:before { 894 | content: "\\eb39"; 895 | } 896 | .codicon-remote:before { 897 | content: "\\eb3a"; 898 | } 899 | .codicon-remove:before { 900 | content: "\\eb3b"; 901 | } 902 | .codicon-replace-all:before { 903 | content: "\\eb3c"; 904 | } 905 | .codicon-replace:before { 906 | content: "\\eb3d"; 907 | } 908 | .codicon-repo-clone:before { 909 | content: "\\eb3e"; 910 | } 911 | .codicon-repo-force-push:before { 912 | content: "\\eb3f"; 913 | } 914 | .codicon-repo-pull:before { 915 | content: "\\eb40"; 916 | } 917 | .codicon-repo-push:before { 918 | content: "\\eb41"; 919 | } 920 | .codicon-report:before { 921 | content: "\\eb42"; 922 | } 923 | .codicon-request-changes:before { 924 | content: "\\eb43"; 925 | } 926 | .codicon-rocket:before { 927 | content: "\\eb44"; 928 | } 929 | .codicon-root-folder-opened:before { 930 | content: "\\eb45"; 931 | } 932 | .codicon-root-folder:before { 933 | content: "\\eb46"; 934 | } 935 | .codicon-rss:before { 936 | content: "\\eb47"; 937 | } 938 | .codicon-ruby:before { 939 | content: "\\eb48"; 940 | } 941 | .codicon-save-all:before { 942 | content: "\\eb49"; 943 | } 944 | .codicon-save-as:before { 945 | content: "\\eb4a"; 946 | } 947 | .codicon-save:before { 948 | content: "\\eb4b"; 949 | } 950 | .codicon-screen-full:before { 951 | content: "\\eb4c"; 952 | } 953 | .codicon-screen-normal:before { 954 | content: "\\eb4d"; 955 | } 956 | .codicon-search-stop:before { 957 | content: "\\eb4e"; 958 | } 959 | .codicon-server:before { 960 | content: "\\eb50"; 961 | } 962 | .codicon-settings-gear:before { 963 | content: "\\eb51"; 964 | } 965 | .codicon-settings:before { 966 | content: "\\eb52"; 967 | } 968 | .codicon-shield:before { 969 | content: "\\eb53"; 970 | } 971 | .codicon-smiley:before { 972 | content: "\\eb54"; 973 | } 974 | .codicon-sort-precedence:before { 975 | content: "\\eb55"; 976 | } 977 | .codicon-split-horizontal:before { 978 | content: "\\eb56"; 979 | } 980 | .codicon-split-vertical:before { 981 | content: "\\eb57"; 982 | } 983 | .codicon-squirrel:before { 984 | content: "\\eb58"; 985 | } 986 | .codicon-star-full:before { 987 | content: "\\eb59"; 988 | } 989 | .codicon-star-half:before { 990 | content: "\\eb5a"; 991 | } 992 | .codicon-symbol-class:before { 993 | content: "\\eb5b"; 994 | } 995 | .codicon-symbol-color:before { 996 | content: "\\eb5c"; 997 | } 998 | .codicon-symbol-constant:before { 999 | content: "\\eb5d"; 1000 | } 1001 | .codicon-symbol-enum-member:before { 1002 | content: "\\eb5e"; 1003 | } 1004 | .codicon-symbol-field:before { 1005 | content: "\\eb5f"; 1006 | } 1007 | .codicon-symbol-file:before { 1008 | content: "\\eb60"; 1009 | } 1010 | .codicon-symbol-interface:before { 1011 | content: "\\eb61"; 1012 | } 1013 | .codicon-symbol-keyword:before { 1014 | content: "\\eb62"; 1015 | } 1016 | .codicon-symbol-misc:before { 1017 | content: "\\eb63"; 1018 | } 1019 | .codicon-symbol-operator:before { 1020 | content: "\\eb64"; 1021 | } 1022 | .codicon-symbol-property:before { 1023 | content: "\\eb65"; 1024 | } 1025 | .codicon-wrench:before { 1026 | content: "\\eb65"; 1027 | } 1028 | .codicon-wrench-subaction:before { 1029 | content: "\\eb65"; 1030 | } 1031 | .codicon-symbol-snippet:before { 1032 | content: "\\eb66"; 1033 | } 1034 | .codicon-tasklist:before { 1035 | content: "\\eb67"; 1036 | } 1037 | .codicon-telescope:before { 1038 | content: "\\eb68"; 1039 | } 1040 | .codicon-text-size:before { 1041 | content: "\\eb69"; 1042 | } 1043 | .codicon-three-bars:before { 1044 | content: "\\eb6a"; 1045 | } 1046 | .codicon-thumbsdown:before { 1047 | content: "\\eb6b"; 1048 | } 1049 | .codicon-thumbsup:before { 1050 | content: "\\eb6c"; 1051 | } 1052 | .codicon-tools:before { 1053 | content: "\\eb6d"; 1054 | } 1055 | .codicon-triangle-down:before { 1056 | content: "\\eb6e"; 1057 | } 1058 | .codicon-triangle-left:before { 1059 | content: "\\eb6f"; 1060 | } 1061 | .codicon-triangle-right:before { 1062 | content: "\\eb70"; 1063 | } 1064 | .codicon-triangle-up:before { 1065 | content: "\\eb71"; 1066 | } 1067 | .codicon-twitter:before { 1068 | content: "\\eb72"; 1069 | } 1070 | .codicon-unfold:before { 1071 | content: "\\eb73"; 1072 | } 1073 | .codicon-unlock:before { 1074 | content: "\\eb74"; 1075 | } 1076 | .codicon-unmute:before { 1077 | content: "\\eb75"; 1078 | } 1079 | .codicon-unverified:before { 1080 | content: "\\eb76"; 1081 | } 1082 | .codicon-verified:before { 1083 | content: "\\eb77"; 1084 | } 1085 | .codicon-versions:before { 1086 | content: "\\eb78"; 1087 | } 1088 | .codicon-vm-active:before { 1089 | content: "\\eb79"; 1090 | } 1091 | .codicon-vm-outline:before { 1092 | content: "\\eb7a"; 1093 | } 1094 | .codicon-vm-running:before { 1095 | content: "\\eb7b"; 1096 | } 1097 | .codicon-watch:before { 1098 | content: "\\eb7c"; 1099 | } 1100 | .codicon-whitespace:before { 1101 | content: "\\eb7d"; 1102 | } 1103 | .codicon-whole-word:before { 1104 | content: "\\eb7e"; 1105 | } 1106 | .codicon-window:before { 1107 | content: "\\eb7f"; 1108 | } 1109 | .codicon-word-wrap:before { 1110 | content: "\\eb80"; 1111 | } 1112 | .codicon-zoom-in:before { 1113 | content: "\\eb81"; 1114 | } 1115 | .codicon-zoom-out:before { 1116 | content: "\\eb82"; 1117 | } 1118 | .codicon-list-filter:before { 1119 | content: "\\eb83"; 1120 | } 1121 | .codicon-list-flat:before { 1122 | content: "\\eb84"; 1123 | } 1124 | .codicon-list-selection:before { 1125 | content: "\\eb85"; 1126 | } 1127 | .codicon-selection:before { 1128 | content: "\\eb85"; 1129 | } 1130 | .codicon-list-tree:before { 1131 | content: "\\eb86"; 1132 | } 1133 | .codicon-debug-breakpoint-function-unverified:before { 1134 | content: "\\eb87"; 1135 | } 1136 | .codicon-debug-breakpoint-function:before { 1137 | content: "\\eb88"; 1138 | } 1139 | .codicon-debug-breakpoint-function-disabled:before { 1140 | content: "\\eb88"; 1141 | } 1142 | .codicon-debug-stackframe-active:before { 1143 | content: "\\eb89"; 1144 | } 1145 | .codicon-debug-stackframe-dot:before { 1146 | content: "\\eb8a"; 1147 | } 1148 | .codicon-debug-stackframe:before { 1149 | content: "\\eb8b"; 1150 | } 1151 | .codicon-debug-stackframe-focused:before { 1152 | content: "\\eb8b"; 1153 | } 1154 | .codicon-debug-breakpoint-unsupported:before { 1155 | content: "\\eb8c"; 1156 | } 1157 | .codicon-symbol-string:before { 1158 | content: "\\eb8d"; 1159 | } 1160 | .codicon-debug-reverse-continue:before { 1161 | content: "\\eb8e"; 1162 | } 1163 | .codicon-debug-step-back:before { 1164 | content: "\\eb8f"; 1165 | } 1166 | .codicon-debug-restart-frame:before { 1167 | content: "\\eb90"; 1168 | } 1169 | .codicon-debug-alt:before { 1170 | content: "\\eb91"; 1171 | } 1172 | .codicon-call-incoming:before { 1173 | content: "\\eb92"; 1174 | } 1175 | .codicon-call-outgoing:before { 1176 | content: "\\eb93"; 1177 | } 1178 | .codicon-menu:before { 1179 | content: "\\eb94"; 1180 | } 1181 | .codicon-expand-all:before { 1182 | content: "\\eb95"; 1183 | } 1184 | .codicon-feedback:before { 1185 | content: "\\eb96"; 1186 | } 1187 | .codicon-group-by-ref-type:before { 1188 | content: "\\eb97"; 1189 | } 1190 | .codicon-ungroup-by-ref-type:before { 1191 | content: "\\eb98"; 1192 | } 1193 | .codicon-account:before { 1194 | content: "\\eb99"; 1195 | } 1196 | .codicon-bell-dot:before { 1197 | content: "\\eb9a"; 1198 | } 1199 | .codicon-debug-console:before { 1200 | content: "\\eb9b"; 1201 | } 1202 | .codicon-library:before { 1203 | content: "\\eb9c"; 1204 | } 1205 | .codicon-output:before { 1206 | content: "\\eb9d"; 1207 | } 1208 | .codicon-run-all:before { 1209 | content: "\\eb9e"; 1210 | } 1211 | .codicon-sync-ignored:before { 1212 | content: "\\eb9f"; 1213 | } 1214 | .codicon-pinned:before { 1215 | content: "\\eba0"; 1216 | } 1217 | .codicon-github-inverted:before { 1218 | content: "\\eba1"; 1219 | } 1220 | .codicon-server-process:before { 1221 | content: "\\eba2"; 1222 | } 1223 | .codicon-server-environment:before { 1224 | content: "\\eba3"; 1225 | } 1226 | .codicon-pass:before { 1227 | content: "\\eba4"; 1228 | } 1229 | .codicon-issue-closed:before { 1230 | content: "\\eba4"; 1231 | } 1232 | .codicon-stop-circle:before { 1233 | content: "\\eba5"; 1234 | } 1235 | .codicon-play-circle:before { 1236 | content: "\\eba6"; 1237 | } 1238 | .codicon-record:before { 1239 | content: "\\eba7"; 1240 | } 1241 | .codicon-debug-alt-small:before { 1242 | content: "\\eba8"; 1243 | } 1244 | .codicon-vm-connect:before { 1245 | content: "\\eba9"; 1246 | } 1247 | .codicon-cloud:before { 1248 | content: "\\ebaa"; 1249 | } 1250 | .codicon-merge:before { 1251 | content: "\\ebab"; 1252 | } 1253 | .codicon-export:before { 1254 | content: "\\ebac"; 1255 | } 1256 | .codicon-graph-left:before { 1257 | content: "\\ebad"; 1258 | } 1259 | .codicon-magnet:before { 1260 | content: "\\ebae"; 1261 | } 1262 | .codicon-notebook:before { 1263 | content: "\\ebaf"; 1264 | } 1265 | .codicon-redo:before { 1266 | content: "\\ebb0"; 1267 | } 1268 | .codicon-check-all:before { 1269 | content: "\\ebb1"; 1270 | } 1271 | .codicon-pinned-dirty:before { 1272 | content: "\\ebb2"; 1273 | } 1274 | .codicon-pass-filled:before { 1275 | content: "\\ebb3"; 1276 | } 1277 | .codicon-circle-large-filled:before { 1278 | content: "\\ebb4"; 1279 | } 1280 | .codicon-circle-large-outline:before { 1281 | content: "\\ebb5"; 1282 | } 1283 | .codicon-combine:before { 1284 | content: "\\ebb6"; 1285 | } 1286 | .codicon-gather:before { 1287 | content: "\\ebb6"; 1288 | } 1289 | .codicon-table:before { 1290 | content: "\\ebb7"; 1291 | } 1292 | .codicon-variable-group:before { 1293 | content: "\\ebb8"; 1294 | } 1295 | .codicon-type-hierarchy:before { 1296 | content: "\\ebb9"; 1297 | } 1298 | .codicon-type-hierarchy-sub:before { 1299 | content: "\\ebba"; 1300 | } 1301 | .codicon-type-hierarchy-super:before { 1302 | content: "\\ebbb"; 1303 | } 1304 | .codicon-git-pull-request-create:before { 1305 | content: "\\ebbc"; 1306 | } 1307 | .codicon-run-above:before { 1308 | content: "\\ebbd"; 1309 | } 1310 | .codicon-run-below:before { 1311 | content: "\\ebbe"; 1312 | } 1313 | .codicon-notebook-template:before { 1314 | content: "\\ebbf"; 1315 | } 1316 | .codicon-debug-rerun:before { 1317 | content: "\\ebc0"; 1318 | } 1319 | .codicon-workspace-trusted:before { 1320 | content: "\\ebc1"; 1321 | } 1322 | .codicon-workspace-untrusted:before { 1323 | content: "\\ebc2"; 1324 | } 1325 | .codicon-workspace-unknown:before { 1326 | content: "\\ebc3"; 1327 | } 1328 | .codicon-terminal-cmd:before { 1329 | content: "\\ebc4"; 1330 | } 1331 | .codicon-terminal-debian:before { 1332 | content: "\\ebc5"; 1333 | } 1334 | .codicon-terminal-linux:before { 1335 | content: "\\ebc6"; 1336 | } 1337 | .codicon-terminal-powershell:before { 1338 | content: "\\ebc7"; 1339 | } 1340 | .codicon-terminal-tmux:before { 1341 | content: "\\ebc8"; 1342 | } 1343 | .codicon-terminal-ubuntu:before { 1344 | content: "\\ebc9"; 1345 | } 1346 | .codicon-terminal-bash:before { 1347 | content: "\\ebca"; 1348 | } 1349 | .codicon-arrow-swap:before { 1350 | content: "\\ebcb"; 1351 | } 1352 | .codicon-copy:before { 1353 | content: "\\ebcc"; 1354 | } 1355 | .codicon-person-add:before { 1356 | content: "\\ebcd"; 1357 | } 1358 | .codicon-filter-filled:before { 1359 | content: "\\ebce"; 1360 | } 1361 | .codicon-wand:before { 1362 | content: "\\ebcf"; 1363 | } 1364 | .codicon-debug-line-by-line:before { 1365 | content: "\\ebd0"; 1366 | } 1367 | .codicon-inspect:before { 1368 | content: "\\ebd1"; 1369 | } 1370 | .codicon-layers:before { 1371 | content: "\\ebd2"; 1372 | } 1373 | .codicon-layers-dot:before { 1374 | content: "\\ebd3"; 1375 | } 1376 | .codicon-layers-active:before { 1377 | content: "\\ebd4"; 1378 | } 1379 | .codicon-compass:before { 1380 | content: "\\ebd5"; 1381 | } 1382 | .codicon-compass-dot:before { 1383 | content: "\\ebd6"; 1384 | } 1385 | .codicon-compass-active:before { 1386 | content: "\\ebd7"; 1387 | } 1388 | .codicon-azure:before { 1389 | content: "\\ebd8"; 1390 | } 1391 | .codicon-issue-draft:before { 1392 | content: "\\ebd9"; 1393 | } 1394 | .codicon-git-pull-request-closed:before { 1395 | content: "\\ebda"; 1396 | } 1397 | .codicon-git-pull-request-draft:before { 1398 | content: "\\ebdb"; 1399 | } 1400 | .codicon-debug-all:before { 1401 | content: "\\ebdc"; 1402 | } 1403 | .codicon-debug-coverage:before { 1404 | content: "\\ebdd"; 1405 | } 1406 | .codicon-run-errors:before { 1407 | content: "\\ebde"; 1408 | } 1409 | .codicon-folder-library:before { 1410 | content: "\\ebdf"; 1411 | } 1412 | .codicon-debug-continue-small:before { 1413 | content: "\\ebe0"; 1414 | } 1415 | .codicon-beaker-stop:before { 1416 | content: "\\ebe1"; 1417 | } 1418 | .codicon-graph-line:before { 1419 | content: "\\ebe2"; 1420 | } 1421 | .codicon-graph-scatter:before { 1422 | content: "\\ebe3"; 1423 | } 1424 | .codicon-pie-chart:before { 1425 | content: "\\ebe4"; 1426 | } 1427 | .codicon-bracket:before { 1428 | content: "\\eb0f"; 1429 | } 1430 | .codicon-bracket-dot:before { 1431 | content: "\\ebe5"; 1432 | } 1433 | .codicon-bracket-error:before { 1434 | content: "\\ebe6"; 1435 | } 1436 | `; 1437 | -------------------------------------------------------------------------------- /src/browser/iconDefinitions.js: -------------------------------------------------------------------------------- 1 | export default { 2 | defaultIcon: { name: "file" }, 3 | icons: [ 4 | { 5 | name: "html", 6 | fileExtensions: ["html", "htm", "xhtml", "html_vm", "asp"], 7 | }, 8 | { name: "pug", fileExtensions: ["jade", "pug"] }, 9 | { 10 | name: "markdown", 11 | fileExtensions: ["md", "markdown", "rst"], 12 | }, 13 | { name: "blink", fileExtensions: ["blink"], light: true }, 14 | { name: "css", fileExtensions: ["css"] }, 15 | { name: "sass", fileExtensions: ["scss", "sass"] }, 16 | { name: "less", fileExtensions: ["less"] }, 17 | { 18 | name: "json", 19 | fileExtensions: ["json", "tsbuildinfo"], 20 | fileNames: [ 21 | ".jscsrc", 22 | ".jshintrc", 23 | "tsconfig.json", 24 | "tslint.json", 25 | "composer.lock", 26 | ".jsbeautifyrc", 27 | ".esformatter", 28 | "cdp.pid", 29 | ], 30 | }, 31 | { 32 | name: "jinja", 33 | fileExtensions: ["jinja", "jinja2", "j2"], 34 | light: true, 35 | }, 36 | { 37 | name: "sublime", 38 | fileExtensions: ["sublime-project", "sublime-workspace"], 39 | }, 40 | { name: "yaml", fileExtensions: ["yaml", "YAML-tmLanguage", "yml"] }, 41 | { 42 | name: "xml", 43 | fileExtensions: [ 44 | "xml", 45 | "plist", 46 | "xsd", 47 | "dtd", 48 | "xsl", 49 | "xslt", 50 | "resx", 51 | "iml", 52 | "xquery", 53 | "tmLanguage", 54 | "manifest", 55 | "project", 56 | ], 57 | fileNames: [".htaccess"], 58 | }, 59 | { 60 | name: "image", 61 | fileExtensions: [ 62 | "png", 63 | "jpeg", 64 | "jpg", 65 | "gif", 66 | "ico", 67 | "tif", 68 | "tiff", 69 | "psd", 70 | "psb", 71 | "ami", 72 | "apx", 73 | "bmp", 74 | "bpg", 75 | "brk", 76 | "cur", 77 | "dds", 78 | "dng", 79 | "exr", 80 | "fpx", 81 | "gbr", 82 | "img", 83 | "jbig2", 84 | "jb2", 85 | "jng", 86 | "jxr", 87 | "pbm", 88 | "pgf", 89 | "pic", 90 | "raw", 91 | "webp", 92 | "eps", 93 | ], 94 | }, 95 | { name: "javascript", fileExtensions: ["js", "esx", "mjs"] }, 96 | { name: "react", fileExtensions: ["jsx"] }, 97 | { name: "react_ts", fileExtensions: ["tsx"] }, 98 | { 99 | name: "settings", 100 | fileExtensions: [ 101 | "ini", 102 | "dlc", 103 | "dll", 104 | "config", 105 | "conf", 106 | "properties", 107 | "prop", 108 | "settings", 109 | "option", 110 | "props", 111 | "toml", 112 | "prefs", 113 | "sln.dotsettings", 114 | "sln.dotsettings.user", 115 | "cfg", 116 | ], 117 | fileNames: [ 118 | ".jshintignore", 119 | ".buildignore", 120 | ".mrconfig", 121 | ".yardopts", 122 | "manifest.mf", 123 | ".clang-format", 124 | ".clang-tidy", 125 | ], 126 | }, 127 | { name: "typescript", fileExtensions: ["ts"] }, 128 | { name: "typescript-def", fileExtensions: ["d.ts"] }, 129 | { name: "markojs", fileExtensions: ["marko"] }, 130 | { name: "pdf", fileExtensions: ["pdf"] }, 131 | { name: "table", fileExtensions: ["xlsx", "xls", "csv", "tsv"] }, 132 | { 133 | name: "vscode", 134 | fileExtensions: [ 135 | "vscodeignore", 136 | "vsixmanifest", 137 | "vsix", 138 | "code-workplace", 139 | ], 140 | }, 141 | { 142 | name: "visualstudio", 143 | fileExtensions: [ 144 | "csproj", 145 | "ruleset", 146 | "sln", 147 | "suo", 148 | "vb", 149 | "vbs", 150 | "vcxitems", 151 | "vcxitems.filters", 152 | "vcxproj", 153 | "vcxproj.filters", 154 | ], 155 | }, 156 | { 157 | name: "database", 158 | fileExtensions: [ 159 | "pdb", 160 | "sql", 161 | "pks", 162 | "pkb", 163 | "accdb", 164 | "mdb", 165 | "sqlite", 166 | "pgsql", 167 | "postgres", 168 | "psql", 169 | ], 170 | }, 171 | { name: "csharp", fileExtensions: ["cs", "csx"] }, 172 | { 173 | name: "zip", 174 | fileExtensions: [ 175 | "zip", 176 | "tar", 177 | "gz", 178 | "xz", 179 | "bzip2", 180 | "gzip", 181 | "7z", 182 | "rar", 183 | "tgz", 184 | ], 185 | }, 186 | { name: "exe", fileExtensions: ["exe", "msi"] }, 187 | { name: "java", fileExtensions: ["java", "jar", "jsp"] }, 188 | { name: "c", fileExtensions: ["c", "m"] }, 189 | { name: "h", fileExtensions: ["h"] }, 190 | { name: "cpp", fileExtensions: ["cc", "cpp", "mm", "cxx"] }, 191 | { name: "hpp", fileExtensions: ["hpp"] }, 192 | { name: "go", fileExtensions: ["go"] }, 193 | { name: "python", fileExtensions: ["py"] }, 194 | { 195 | name: "python-misc", 196 | fileExtensions: ["pyc", "whl"], 197 | fileNames: [ 198 | "requirements.txt", 199 | "pipfile", 200 | ".python-version", 201 | "manifest.in", 202 | ], 203 | }, 204 | { name: "url", fileExtensions: ["url"] }, 205 | { 206 | name: "console", 207 | fileExtensions: [ 208 | "sh", 209 | "ksh", 210 | "csh", 211 | "tcsh", 212 | "zsh", 213 | "bash", 214 | "bat", 215 | "cmd", 216 | "awk", 217 | "fish", 218 | ], 219 | }, 220 | { 221 | name: "powershell", 222 | fileExtensions: ["ps1", "psm1", "psd1", "ps1xml", "psc1", "pssc"], 223 | }, 224 | { 225 | name: "gradle", 226 | fileExtensions: ["gradle"], 227 | fileNames: ["gradle.properties", "gradlew", "gradle-wrapper.properties"], 228 | }, 229 | { name: "word", fileExtensions: ["doc", "docx", "rtf"] }, 230 | { 231 | name: "certificate", 232 | fileExtensions: ["cer", "cert", "crt"], 233 | fileNames: [ 234 | "license", 235 | "license.md", 236 | "license.txt", 237 | "licence", 238 | "licence.md", 239 | "licence.txt", 240 | ], 241 | }, 242 | { 243 | name: "key", 244 | fileExtensions: ["pub", "key", "pem", "asc", "gpg"], 245 | fileNames: [".htpasswd"], 246 | }, 247 | { 248 | name: "font", 249 | fileExtensions: [ 250 | "woff", 251 | "woff2", 252 | "ttf", 253 | "eot", 254 | "suit", 255 | "otf", 256 | "bmap", 257 | "fnt", 258 | "odttf", 259 | "ttc", 260 | "font", 261 | "fonts", 262 | "sui", 263 | "ntf", 264 | "mrf", 265 | ], 266 | }, 267 | { name: "lib", fileExtensions: ["lib", "bib"] }, 268 | { name: "ruby", fileExtensions: ["rb", "erb"] }, 269 | { name: "gemfile", fileNames: ["gemfile"] }, 270 | { name: "fsharp", fileExtensions: ["fs", "fsx", "fsi", "fsproj"] }, 271 | { name: "swift", fileExtensions: ["swift"] }, 272 | { name: "arduino", fileExtensions: ["ino"] }, 273 | { 274 | name: "docker", 275 | fileExtensions: ["dockerignore", "dockerfile"], 276 | fileNames: [ 277 | "dockerfile", 278 | "docker-compose.yml", 279 | "docker-compose.yaml", 280 | "docker-compose.dev.yml", 281 | "docker-compose.local.yml", 282 | "docker-compose.ci.yml", 283 | "docker-compose.override.yml", 284 | "docker-compose.staging.yml", 285 | "docker-compose.prod.yml", 286 | "docker-compose.production.yml", 287 | "docker-compose.test.yml", 288 | ], 289 | }, 290 | { name: "tex", fileExtensions: ["tex", "cls", "sty"] }, 291 | { 292 | name: "powerpoint", 293 | fileExtensions: [ 294 | "pptx", 295 | "ppt", 296 | "pptm", 297 | "potx", 298 | "potm", 299 | "ppsx", 300 | "ppsm", 301 | "pps", 302 | "ppam", 303 | "ppa", 304 | ], 305 | }, 306 | { 307 | name: "video", 308 | fileExtensions: [ 309 | "webm", 310 | "mkv", 311 | "flv", 312 | "vob", 313 | "ogv", 314 | "ogg", 315 | "gifv", 316 | "avi", 317 | "mov", 318 | "qt", 319 | "wmv", 320 | "yuv", 321 | "rm", 322 | "rmvb", 323 | "mp4", 324 | "m4v", 325 | "mpg", 326 | "mp2", 327 | "mpeg", 328 | "mpe", 329 | "mpv", 330 | "m2v", 331 | ], 332 | }, 333 | { name: "virtual", fileExtensions: ["vdi", "vbox", "vbox-prev"] }, 334 | { name: "email", fileExtensions: ["ics"], fileNames: [".mailmap"] }, 335 | { name: "audio", fileExtensions: ["mp3", "flac", "m4a", "wma", "aiff"] }, 336 | { name: "coffee", fileExtensions: ["coffee", "cson", "iced"] }, 337 | { name: "document", fileExtensions: ["txt"] }, 338 | { 339 | name: "graphql", 340 | fileExtensions: ["graphql", "gql"], 341 | fileNames: [".graphqlconfig"], 342 | }, 343 | { name: "rust", fileExtensions: ["rs"] }, 344 | { name: "raml", fileExtensions: ["raml"] }, 345 | { name: "xaml", fileExtensions: ["xaml"] }, 346 | { name: "haskell", fileExtensions: ["hs"] }, 347 | { name: "kotlin", fileExtensions: ["kt", "kts"] }, 348 | { 349 | name: "git", 350 | fileExtensions: ["patch"], 351 | fileNames: [ 352 | ".gitignore", 353 | ".gitconfig", 354 | ".gitattributes", 355 | ".gitmodules", 356 | ".gitkeep", 357 | "git-history", 358 | ], 359 | }, 360 | { name: "lua", fileExtensions: ["lua"], fileNames: [".luacheckrc"] }, 361 | { name: "clojure", fileExtensions: ["clj", "cljs", "cljc"] }, 362 | { name: "groovy", fileExtensions: ["groovy"] }, 363 | { name: "r", fileExtensions: ["r", "rmd"], fileNames: [".Rhistory"] }, 364 | { name: "dart", fileExtensions: ["dart"] }, 365 | { name: "actionscript", fileExtensions: ["as"] }, 366 | { name: "mxml", fileExtensions: ["mxml"] }, 367 | { name: "autohotkey", fileExtensions: ["ahk"] }, 368 | { name: "flash", fileExtensions: ["swf"] }, 369 | { name: "swc", fileExtensions: ["swc"] }, 370 | { 371 | name: "cmake", 372 | fileExtensions: ["cmake"], 373 | fileNames: ["cmakelists.txt", "cmakecache.txt"], 374 | }, 375 | { 376 | name: "assembly", 377 | fileExtensions: [ 378 | "asm", 379 | "a51", 380 | "inc", 381 | "nasm", 382 | "s", 383 | "ms", 384 | "agc", 385 | "ags", 386 | "aea", 387 | "argus", 388 | "mitigus", 389 | "binsource", 390 | ], 391 | }, 392 | { name: "vue", fileExtensions: ["vue"] }, 393 | { name: "vue-config", fileNames: ["vue.config.js", "vue.config.ts"] }, 394 | { name: "ocaml", fileExtensions: ["ml", "mli", "cmx"] }, 395 | { name: "javascript-map", fileExtensions: ["js.map", "mjs.map"] }, 396 | { name: "css-map", fileExtensions: ["css.map"] }, 397 | { name: "lock", fileExtensions: ["lock"] }, 398 | { name: "handlebars", fileExtensions: ["hbs", "mustache"] }, 399 | { name: "perl", fileExtensions: ["pl", "pm"] }, 400 | { name: "haxe", fileExtensions: ["hx"] }, 401 | { name: "test-ts", fileExtensions: ["spec.ts", "test.ts", "ts.snap"] }, 402 | { 403 | name: "test-jsx", 404 | fileExtensions: [ 405 | "spec.tsx", 406 | "test.tsx", 407 | "tsx.snap", 408 | "spec.jsx", 409 | "test.jsx", 410 | "jsx.snap", 411 | ], 412 | }, 413 | { name: "test-js", fileExtensions: ["spec.js", "test.js", "js.snap"] }, 414 | { name: "puppet", fileExtensions: ["pp"] }, 415 | { name: "elixir", fileExtensions: ["ex", "exs", "eex", "leex"] }, 416 | { name: "livescript", fileExtensions: ["ls"] }, 417 | { name: "erlang", fileExtensions: ["erl"] }, 418 | { name: "twig", fileExtensions: ["twig"] }, 419 | { name: "julia", fileExtensions: ["jl"] }, 420 | { name: "elm", fileExtensions: ["elm"] }, 421 | { name: "purescript", fileExtensions: ["pure", "purs"] }, 422 | { name: "smarty", fileExtensions: ["tpl"] }, 423 | { name: "stylus", fileExtensions: ["styl"] }, 424 | { name: "reason", fileExtensions: ["re", "rei"] }, 425 | { name: "bucklescript", fileExtensions: ["cmj"] }, 426 | { name: "merlin", fileExtensions: ["merlin"] }, 427 | { name: "verilog", fileExtensions: ["v", "vhd", "sv", "svh"] }, 428 | { name: "mathematica", fileExtensions: ["nb"] }, 429 | { name: "wolframlanguage", fileExtensions: ["wl", "wls"] }, 430 | { name: "nunjucks", fileExtensions: ["njk", "nunjucks"] }, 431 | { name: "robot", fileExtensions: ["robot"] }, 432 | { name: "solidity", fileExtensions: ["sol"] }, 433 | { name: "autoit", fileExtensions: ["au3"] }, 434 | { name: "haml", fileExtensions: ["haml"] }, 435 | { name: "yang", fileExtensions: ["yang"] }, 436 | { name: "mjml", fileExtensions: ["mjml"] }, 437 | { 438 | name: "terraform", 439 | fileExtensions: ["tf", "tf.json", "tfvars", "tfstate"], 440 | }, 441 | { name: "laravel", fileExtensions: ["blade.php", "inky.php"] }, 442 | { name: "applescript", fileExtensions: ["applescript"] }, 443 | { name: "cake", fileExtensions: ["cake"] }, 444 | { name: "cucumber", fileExtensions: ["feature"] }, 445 | { name: "nim", fileExtensions: ["nim", "nimble"] }, 446 | { name: "apiblueprint", fileExtensions: ["apib", "apiblueprint"] }, 447 | { name: "riot", fileExtensions: ["tag"] }, 448 | { name: "vfl", fileExtensions: ["vfl"], fileNames: [".vfl"] }, 449 | { name: "kl", fileExtensions: ["kl"], fileNames: [".kl"] }, 450 | { 451 | name: "postcss", 452 | fileExtensions: ["pcss", "sss"], 453 | fileNames: ["postcss.config.js", ".postcssrc.js", ".postcssrc"], 454 | }, 455 | { name: "todo", fileExtensions: ["todo"] }, 456 | { name: "coldfusion", fileExtensions: ["cfml", "cfc", "lucee", "cfm"] }, 457 | { name: "cabal", fileExtensions: ["cabal"] }, 458 | { name: "nix", fileExtensions: ["nix"] }, 459 | { name: "slim", fileExtensions: ["slim"] }, 460 | { name: "http", fileExtensions: ["http", "rest"] }, 461 | { name: "restql", fileExtensions: ["rql", "restql"] }, 462 | { name: "kivy", fileExtensions: ["kv"] }, 463 | { 464 | name: "graphcool", 465 | fileExtensions: ["graphcool"], 466 | fileNames: ["project.graphcool"], 467 | }, 468 | { name: "sbt", fileExtensions: ["sbt"] }, 469 | { 470 | name: "webpack", 471 | fileNames: [ 472 | "webpack.js", 473 | "webpack.ts", 474 | "webpack.base.js", 475 | "webpack.base.ts", 476 | "webpack.config.js", 477 | "webpack.config.ts", 478 | "webpack.common.js", 479 | "webpack.common.ts", 480 | "webpack.config.common.js", 481 | "webpack.config.common.ts", 482 | "webpack.config.common.babel.js", 483 | "webpack.config.common.babel.ts", 484 | "webpack.dev.js", 485 | "webpack.dev.ts", 486 | "webpack.config.dev.js", 487 | "webpack.config.dev.ts", 488 | "webpack.config.dev.babel.js", 489 | "webpack.config.dev.babel.ts", 490 | "webpack.prod.js", 491 | "webpack.prod.ts", 492 | "webpack.server.js", 493 | "webpack.server.ts", 494 | "webpack.client.js", 495 | "webpack.client.ts", 496 | "webpack.config.server.js", 497 | "webpack.config.server.ts", 498 | "webpack.config.client.js", 499 | "webpack.config.client.ts", 500 | "webpack.config.production.babel.js", 501 | "webpack.config.production.babel.ts", 502 | "webpack.config.prod.babel.js", 503 | "webpack.config.prod.babel.ts", 504 | "webpack.config.prod.js", 505 | "webpack.config.prod.ts", 506 | "webpack.config.production.js", 507 | "webpack.config.production.ts", 508 | "webpack.config.staging.js", 509 | "webpack.config.staging.ts", 510 | "webpack.config.babel.js", 511 | "webpack.config.babel.ts", 512 | "webpack.config.base.babel.js", 513 | "webpack.config.base.babel.ts", 514 | "webpack.config.base.js", 515 | "webpack.config.base.ts", 516 | "webpack.config.staging.babel.js", 517 | "webpack.config.staging.babel.ts", 518 | "webpack.config.coffee", 519 | "webpack.config.test.js", 520 | "webpack.config.test.ts", 521 | "webpack.config.vendor.js", 522 | "webpack.config.vendor.ts", 523 | "webpack.config.vendor.production.js", 524 | "webpack.config.vendor.production.ts", 525 | "webpack.test.js", 526 | "webpack.test.ts", 527 | "webpack.dist.js", 528 | "webpack.dist.ts", 529 | "webpackfile.js", 530 | "webpackfile.ts", 531 | ], 532 | }, 533 | { name: "ionic", fileNames: ["ionic.config.json", ".io-config.json"] }, 534 | { 535 | name: "gulp", 536 | fileNames: ["gulpfile.js", "gulpfile.ts", "gulpfile.babel.js"], 537 | }, 538 | { 539 | name: "nodejs", 540 | fileNames: ["package.json", "package-lock.json", ".nvmrc", ".esmrc"], 541 | }, 542 | { name: "npm", fileNames: [".npmignore", ".npmrc"] }, 543 | { 544 | name: "yarn", 545 | fileNames: [ 546 | ".yarnrc", 547 | "yarn.lock", 548 | ".yarnclean", 549 | ".yarn-integrity", 550 | "yarn-error.log", 551 | ], 552 | }, 553 | { name: "android", fileNames: ["androidmanifest.xml"] }, 554 | { 555 | name: "tune", 556 | fileNames: [ 557 | ".env", 558 | ".env.example", 559 | ".env.local", 560 | ".env.dev", 561 | ".env.development", 562 | ".env.prod", 563 | ".env.production", 564 | ".env.staging", 565 | ".env.preview", 566 | ".env.test", 567 | ".env.development.local", 568 | ".env.production.local", 569 | ".env.test.local", 570 | ], 571 | }, 572 | { 573 | name: "babel", 574 | fileNames: [".babelrc", ".babelrc.js", "babel.config.js"], 575 | }, 576 | { 577 | name: "contributing", 578 | fileNames: ["contributing.md"], 579 | }, 580 | { name: "readme", fileNames: ["readme.md", "readme.txt", "readme"] }, 581 | { 582 | name: "changelog", 583 | fileNames: ["changelog", "changelog.md", "changelog.txt"], 584 | }, 585 | { 586 | name: "credits", 587 | fileNames: ["credits", "credits.txt", "credits.md"], 588 | }, 589 | { 590 | name: "authors", 591 | fileNames: ["authors", "authors.md", "authors.txt"], 592 | }, 593 | { name: "flow", fileNames: [".flowconfig"] }, 594 | { name: "favicon", fileNames: ["favicon.ico"] }, 595 | { 596 | name: "karma", 597 | fileNames: [ 598 | "karma.conf.js", 599 | "karma.conf.ts", 600 | "karma.conf.coffee", 601 | "karma.config.js", 602 | "karma.config.ts", 603 | "karma-main.js", 604 | "karma-main.ts", 605 | ], 606 | }, 607 | { name: "bithound", fileNames: [".bithoundrc"] }, 608 | { name: "appveyor", fileNames: [".appveyor.yml", "appveyor.yml"] }, 609 | { name: "travis", fileNames: [".travis.yml"] }, 610 | { 611 | name: "protractor", 612 | fileNames: [ 613 | "protractor.conf.js", 614 | "protractor.conf.ts", 615 | "protractor.conf.coffee", 616 | "protractor.config.js", 617 | "protractor.config.ts", 618 | ], 619 | }, 620 | { name: "fusebox", fileNames: ["fuse.js"] }, 621 | { name: "heroku", fileNames: ["procfile", "procfile.windows"] }, 622 | { name: "editorconfig", fileNames: [".editorconfig"] }, 623 | { name: "gitlab", fileNames: [".gitlab-ci.yml"] }, 624 | { name: "bower", fileNames: [".bowerrc", "bower.json"] }, 625 | { 626 | name: "eslint", 627 | fileNames: [ 628 | ".eslintrc.js", 629 | ".eslintrc.yaml", 630 | ".eslintrc.yml", 631 | ".eslintrc.json", 632 | ".eslintrc", 633 | ".eslintignore", 634 | ], 635 | }, 636 | { 637 | name: "conduct", 638 | fileNames: ["code_of_conduct.md", "code_of_conduct.txt"], 639 | }, 640 | { name: "watchman", fileNames: [".watchmanconfig"] }, 641 | { name: "aurelia", fileNames: ["aurelia.json"] }, 642 | { name: "mocha", fileNames: ["mocha.opts"] }, 643 | { name: "jenkins", fileNames: ["jenkinsfile"] }, 644 | { name: "firebase", fileNames: ["firebase.json", ".firebaserc"] }, 645 | { 646 | name: "rollup", 647 | fileNames: [ 648 | "rollup.config.js", 649 | "rollup.config.ts", 650 | "rollup-config.js", 651 | "rollup-config.ts", 652 | "rollup.config.common.js", 653 | "rollup.config.common.ts", 654 | "rollup.config.base.js", 655 | "rollup.config.base.ts", 656 | "rollup.config.prod.js", 657 | "rollup.config.prod.ts", 658 | "rollup.config.dev.js", 659 | "rollup.config.dev.ts", 660 | "rollup.config.prod.vendor.js", 661 | "rollup.config.prod.vendor.ts", 662 | ], 663 | }, 664 | { name: "hack", fileNames: [".hhconfig"] }, 665 | { 666 | name: "stylelint", 667 | fileNames: [ 668 | ".stylelintrc", 669 | "stylelint.config.js", 670 | ".stylelintrc.json", 671 | ".stylelintrc.yaml", 672 | ".stylelintrc.yml", 673 | ".stylelintrc.js", 674 | ".stylelintignore", 675 | ], 676 | light: true, 677 | }, 678 | { name: "code-climate", fileNames: [".codeclimate.yml"], light: true }, 679 | { 680 | name: "prettier", 681 | fileNames: [ 682 | ".prettierrc", 683 | "prettier.config.js", 684 | ".prettierrc.js", 685 | ".prettierrc.json", 686 | ".prettierrc.yaml", 687 | ".prettierrc.yml", 688 | ".prettierignore", 689 | ], 690 | }, 691 | { name: "nodemon", fileNames: ["nodemon.json"] }, 692 | { name: "webhint", fileNames: [".hintrc"] }, 693 | { 694 | name: "browserlist", 695 | fileNames: ["browserslist", ".browserslistrc"], 696 | light: true, 697 | }, 698 | { name: "crystal", fileExtensions: ["cr", "ecr"], light: true }, 699 | { name: "snyk", fileNames: [".snyk"] }, 700 | { 701 | name: "drone", 702 | fileExtensions: ["drone.yml"], 703 | fileNames: [".drone.yml"], 704 | light: true, 705 | }, 706 | { name: "cuda", fileExtensions: ["cu", "cuh"] }, 707 | { name: "log", fileExtensions: ["log"] }, 708 | { name: "dotjs", fileExtensions: ["def", "dot", "jst"] }, 709 | { name: "ejs", fileExtensions: ["ejs"] }, 710 | { name: "sequelize", fileNames: [".sequelizerc"] }, 711 | { 712 | name: "gatsby", 713 | fileNames: [ 714 | "gatsby.config.js", 715 | "gatsby-config.js", 716 | "gatsby-node.js", 717 | "gatsby-browser.js", 718 | "gatsby-ssr.js", 719 | ], 720 | }, 721 | { 722 | name: "wakatime", 723 | fileNames: [".wakatime-project"], 724 | fileExtensions: [".wakatime-project"], 725 | light: true, 726 | }, 727 | { name: "circleci", fileNames: ["circle.yml"], light: true }, 728 | { name: "cloudfoundry", fileNames: [".cfignore"] }, 729 | { 730 | name: "grunt", 731 | fileNames: [ 732 | "gruntfile.js", 733 | "gruntfile.ts", 734 | "gruntfile.coffee", 735 | "gruntfile.babel.js", 736 | "gruntfile.babel.ts", 737 | "gruntfile.babel.coffee", 738 | ], 739 | }, 740 | { 741 | name: "jest", 742 | fileNames: [ 743 | "jest.config.js", 744 | "jest.config.ts", 745 | "jest.config.json", 746 | "jest.setup.js", 747 | "jest.setup.ts", 748 | "jest.json", 749 | ".jestrc", 750 | "jest.teardown.js", 751 | ], 752 | }, 753 | { name: "processing", fileExtensions: ["pde"], light: true }, 754 | { 755 | name: "storybook", 756 | fileExtensions: [ 757 | "stories.js", 758 | "stories.jsx", 759 | "story.js", 760 | "story.jsx", 761 | "stories.ts", 762 | "stories.tsx", 763 | "story.ts", 764 | "story.tsx", 765 | ], 766 | }, 767 | { name: "wepy", fileExtensions: ["wpy"] }, 768 | { name: "fastlane", fileNames: ["fastfile", "appfile"] }, 769 | { name: "hcl", fileExtensions: ["hcl"], light: true }, 770 | { name: "helm", fileNames: [".helmignore"] }, 771 | { name: "san", fileExtensions: ["san"] }, 772 | { name: "wallaby", fileNames: ["wallaby.js", "wallaby.conf.js"] }, 773 | { name: "django", fileExtensions: ["djt"] }, 774 | { 775 | name: "stencil", 776 | fileNames: ["stencil.config.js", "stencil.config.ts"], 777 | light: true, 778 | }, 779 | { name: "red", fileExtensions: ["red"] }, 780 | { name: "makefile", fileNames: ["makefile"] }, 781 | { name: "foxpro", fileExtensions: ["fxp", "prg"] }, 782 | { name: "i18n", fileExtensions: ["pot", "po", "mo"] }, 783 | { name: "webassembly", fileExtensions: ["wat", "wasm"] }, 784 | { 785 | name: "semantic-release", 786 | light: true, 787 | fileNames: [".releaserc", "release.config.js"], 788 | }, 789 | { 790 | name: "bitbucket", 791 | fileNames: ["bitbucket-pipelines.yaml", "bitbucket-pipelines.yml"], 792 | }, 793 | { name: "jupyter", fileExtensions: ["ipynb"] }, 794 | { name: "d", fileExtensions: ["d"] }, 795 | { name: "mdx", fileExtensions: ["mdx"] }, 796 | { name: "ballerina", fileExtensions: ["bal", "balx"] }, 797 | { name: "racket", fileExtensions: ["rkt"] }, 798 | { name: "bazel", fileExtensions: ["bzl", "bazel"] }, 799 | { name: "mint", fileExtensions: ["mint"] }, 800 | { name: "velocity", fileExtensions: ["vm", "fhtml", "vtl"] }, 801 | { name: "godot", fileExtensions: ["gd"] }, 802 | { name: "godot-assets", fileExtensions: ["godot", "tres", "tscn"] }, 803 | { name: "azure-pipelines", fileNames: ["azure-pipelines.yml"] }, 804 | { name: "azure", fileExtensions: ["azcli"] }, 805 | { 806 | name: "vagrant", 807 | fileNames: ["vagrantfile"], 808 | fileExtensions: ["vagrantfile"], 809 | }, 810 | { name: "prisma", fileNames: ["prisma.yml"], fileExtensions: ["prisma"] }, 811 | { name: "razor", fileExtensions: ["cshtml", "vbhtml"] }, 812 | { name: "asciidoc", fileExtensions: ["ad", "adoc", "asciidoc"] }, 813 | { name: "istanbul", fileNames: [".nycrc", ".nycrc.json"] }, 814 | { name: "edge", fileExtensions: ["edge"] }, 815 | { name: "scheme", fileExtensions: ["ss", "scm"] }, 816 | { name: "tailwindcss", fileNames: ["tailwind.js", "tailwind.config.js"] }, 817 | { name: "3d", fileExtensions: ["stl", "obj"] }, 818 | { name: "buildkite", fileNames: ["buildkite.yml", "buildkite.yaml"] }, 819 | { name: "svg", fileExtensions: ["svg"] }, 820 | ], 821 | }; 822 | -------------------------------------------------------------------------------- /src/browser/index.js: -------------------------------------------------------------------------------- 1 | import "./FileTree.js"; 2 | import "./monaco.js"; 3 | import "./sdp-nav/sdp-nav.js"; 4 | import "./card-frame-handler.js"; 5 | -------------------------------------------------------------------------------- /src/browser/monaco.js: -------------------------------------------------------------------------------- 1 | let loaderPending = false; 2 | const loaderCallbacks = []; 3 | 4 | export let monaco; 5 | export let editor; 6 | 7 | function onAmdLoaderLoad() { 8 | let currentCallback = loaderCallbacks.shift(); 9 | while (currentCallback) { 10 | window.clearTimeout(currentCallback.timeout); 11 | currentCallback.resolve(); 12 | currentCallback = loaderCallbacks.shift(); 13 | } 14 | } 15 | 16 | function onAmdLoaderError(err) { 17 | let currentCallback = loaderCallbacks.shift(); 18 | while (currentCallback) { 19 | window.clearTimeout(currentCallback.timeout); 20 | currentCallback.reject(err); 21 | currentCallback = loaderCallbacks.shift(); 22 | } 23 | } 24 | 25 | export function ensureMonacoIsLoaded( 26 | // srcPath = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.29.1/dev' // <-- for debugging 27 | srcPath = "https://cdn.jsdelivr.net/npm/monaco-editor@0.29.1/min" 28 | ) { 29 | return new Promise((resolve, reject) => { 30 | if (monaco) { 31 | resolve(); 32 | return; 33 | } 34 | const config = { 35 | paths: { 36 | vs: srcPath + "/vs", 37 | vs_dev: srcPath.replace(/\/min$/, "/dev") + "/vs", 38 | }, 39 | }; 40 | const loaderUrl = `${config.paths.vs}/loader.js`; 41 | 42 | const timeout = setTimeout(() => { 43 | reject(new Error("Couldn't load monaco editor after 60s")); 44 | }, 60000); 45 | 46 | loaderCallbacks.push({ 47 | resolve: () => { 48 | if (loaderPending) { 49 | window.require.config(config); 50 | loaderPending = false; 51 | } 52 | 53 | // Cross domain workaround - https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-amd-cross.md 54 | window.MonacoEnvironment = { 55 | getWorkerUrl() { 56 | return `data:text/javascript;charset=utf-8,${encodeURIComponent(` 57 | self.MonacoEnvironment = { 58 | baseUrl: '${srcPath}' 59 | }; 60 | importScripts('${srcPath}/vs/base/worker/workerMain.js');`)}`; 61 | }, 62 | }; 63 | 64 | window.require(["vs/editor/editor.main"], resolve); 65 | }, 66 | timeout, 67 | reject, 68 | }); 69 | 70 | if (!loaderPending) { 71 | if (window.require) { 72 | onAmdLoaderLoad(); 73 | } else { 74 | const loaderScript = window.document.createElement("script"); 75 | loaderScript.type = "text/javascript"; 76 | loaderScript.src = loaderUrl; 77 | loaderScript.addEventListener("load", onAmdLoaderLoad); 78 | loaderScript.addEventListener("error", onAmdLoaderError); 79 | window.document.body.appendChild(loaderScript); 80 | loaderPending = true; 81 | } 82 | } 83 | }); 84 | } 85 | 86 | ensureMonacoIsLoaded().then(() => { 87 | monaco = window.monaco; 88 | editor = monaco.editor.create(document.getElementById("monaco-container"), { 89 | theme: "vs-dark", 90 | }); 91 | editor.getModel().updateOptions({ tabSize: 2 }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/sdp-drawer.js: -------------------------------------------------------------------------------- 1 | import { SdpDrawer } from "./src/SdpDrawer.js"; 2 | 3 | customElements.define("sdp-drawer", SdpDrawer); 4 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/sdp-external-links.js: -------------------------------------------------------------------------------- 1 | import { SdpExternalLinks } from "./src/SdpExternalLinks.js"; 2 | 3 | customElements.define("sdp-external-links", SdpExternalLinks); 4 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/sdp-logo.js: -------------------------------------------------------------------------------- 1 | import { SdpLogo } from "./src/SdpLogo.js"; 2 | 3 | customElements.define("sdp-logo", SdpLogo); 4 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/sdp-nav.js: -------------------------------------------------------------------------------- 1 | import { SdpNav } from "./src/SdpNav.js"; 2 | 3 | customElements.define("sdp-nav", SdpNav); 4 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/src/SdpDrawer.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from "@lion/core"; 2 | import { OverlayMixin, withModalDialogConfig } from "@lion/overlays"; 3 | 4 | /** 5 | * Responsive and mobile friendly drawer sidebar menu 6 | * 7 | * Copied from https://github.com/modernweb-dev/rocket/tree/main/packages/drawer 8 | * Inspired by https://github.com/kenchris/websensor-compass/blob/master/scripts/menu-drawer.js 9 | * 10 | * Might be nice to abstract the Gesture drag handling logic in a reusable utility or wrapper component 11 | */ 12 | 13 | /** @typedef {import('@lion/overlays/types/OverlayConfig').OverlayConfig} OverlayConfig */ 14 | 15 | function transitionend(el) { 16 | return new Promise((resolve) => { 17 | el.addEventListener("transitionend", resolve, { once: true }); 18 | }); 19 | } 20 | 21 | export class SdpDrawer extends OverlayMixin(LitElement) { 22 | // eslint-disable-next-line class-methods-use-this 23 | _defineOverlayConfig() { 24 | return /** @type {OverlayConfig} */ { 25 | ...withModalDialogConfig(), 26 | hidesOnOutsideClick: true, 27 | viewportConfig: { 28 | placement: "slide", 29 | }, 30 | }; 31 | } 32 | 33 | _setupOverlayCtrl() { 34 | super._setupOverlayCtrl(); 35 | 36 | /* eslint-disable no-param-reassign */ 37 | this._overlayCtrl.transitionHide = async ({ contentNode }) => { 38 | contentNode.style.transition = 39 | "transform 0.20s cubic-bezier(0.4, 0.0, 0.2, 1)"; 40 | contentNode.style.transform = "translateX(-100%)"; 41 | await transitionend(contentNode); 42 | }; 43 | this._overlayCtrl.transitionShow = async ({ contentNode }) => { 44 | contentNode.style.display = "flex"; 45 | contentNode.style.transition = 46 | "transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1)"; 47 | // wait for display block to be "updated in the dom" and then translate otherwise there will be no animation 48 | await new Promise((resolve) => requestAnimationFrame(resolve)); 49 | contentNode.style.transform = "translateX(0)"; 50 | }; 51 | /* eslint-enable no-param-reassign */ 52 | 53 | this._overlayCtrl.contentNode.style.transition = 54 | "transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1), var(--cwk-background-transition), var(--cwk-fill-transition)"; 55 | this._overlayCtrl.contentNode.style.transform = "translateX(-100%)"; 56 | this._overlayCtrl.contentNode.style.willChange = "transform"; 57 | 58 | // gesture 59 | this.containerEl = this._overlayCtrl.contentNode; 60 | } 61 | 62 | _teardownOverlayCtrl() { 63 | super._teardownOverlayCtrl(); 64 | this._overlayCtrl.contentNode.style.transform = "translateX(0)"; 65 | } 66 | 67 | /** @param {import('lit-element').PropertyValues } changedProperties */ 68 | updated(changedProperties) { 69 | super.updated(changedProperties); 70 | if (changedProperties.has("opened")) { 71 | if (this.opened) { 72 | document.body.addEventListener("touchstart", this.onGestureStart, { 73 | passive: true, 74 | }); 75 | } else { 76 | document.body.removeEventListener("touchstart", this.onGestureStart); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * @param { MediaQueryListEvent } query 83 | */ 84 | onMatchMedia(query) { 85 | if (query.matches && this.opened) { 86 | this.opened = false; 87 | } 88 | } 89 | 90 | // ********************* GESTURE *********************** 91 | 92 | constructor() { 93 | super(); 94 | 95 | this.onMatchMedia = this.onMatchMedia.bind(this); 96 | this.onGestureStart = this.onGestureStart.bind(this); 97 | this.onGestureMove = this.onGestureMove.bind(this); 98 | this.onGestureEnd = this.onGestureEnd.bind(this); 99 | this.updateFromTouch = this.updateFromTouch.bind(this); 100 | 101 | this.mediaMatcher = window.matchMedia("(min-width: 601px)"); 102 | this.mediaMatcher.addEventListener("change", this.onMatchMedia); 103 | 104 | this._startX = 0; 105 | this._currentX = 0; 106 | this._velocity = 0; 107 | this._left = 0; 108 | this.__touching = false; 109 | this._timestamp = 0; 110 | } 111 | 112 | connectedCallback() { 113 | super.connectedCallback(); 114 | this.updateComplete.then(() => { 115 | this._overlayCtrl.contentNode.style.display = "none"; 116 | }); 117 | } 118 | 119 | render() { 120 | return html` 121 | 122 | 123 |
124 | 125 |
126 | `; 127 | } 128 | 129 | /** 130 | * @param {TouchEvent} ev 131 | */ 132 | onGestureStart(ev) { 133 | if (!this.containerEl) { 134 | return; 135 | } 136 | this.__touching = true; 137 | this._left = this.containerEl.getBoundingClientRect().left; 138 | this._startX = ev.targetTouches[0].clientX; 139 | this._currentX = this._startX; 140 | this._timestamp = new Date().getTime(); 141 | this._velocity = 0; 142 | 143 | this._overlayCtrl.contentNode.style.transition = ""; 144 | 145 | document.body.addEventListener("touchmove", this.onGestureMove, { 146 | passive: true, 147 | }); 148 | document.body.addEventListener("touchend", this.onGestureEnd, { 149 | passive: true, 150 | }); 151 | document.body.addEventListener("touchcancel", this.onGestureEnd, { 152 | passive: true, 153 | }); 154 | requestAnimationFrame(this.updateFromTouch); 155 | } 156 | 157 | /** 158 | * @param {number} dDist 159 | * @param {number} dTime 160 | */ 161 | addVelocitySample(dDist, dTime) { 162 | if (dTime === 0) { 163 | return; 164 | } 165 | 166 | const velocitySample = dDist / dTime; 167 | 168 | // Low pass filter. 169 | const alpha = 0.75; 170 | this._velocity *= alpha; 171 | this._velocity += (1 - alpha) * velocitySample; 172 | } 173 | 174 | /** 175 | * @param {TouchEvent} ev 176 | */ 177 | onGestureMove(ev) { 178 | if (!this.__touching) { 179 | return; 180 | } 181 | const lastTimestamp = this._timestamp; 182 | this._timestamp = new Date().getTime(); 183 | const dTime = this._timestamp - lastTimestamp; 184 | const lastX = this._currentX; 185 | this._currentX = ev.targetTouches[0].clientX; 186 | const dX = this._currentX - lastX; 187 | this.addVelocitySample(dX, dTime); 188 | } 189 | 190 | onGestureEnd() { 191 | if (!this.__touching || !this.containerEl) { 192 | this.opened = false; 193 | return; 194 | } 195 | 196 | this.__touching = false; 197 | let endOpenedState; 198 | 199 | // Check for fling. 200 | if (Math.abs(this._velocity) > 1) { 201 | endOpenedState = this._velocity > 0; 202 | } else { 203 | // Check depending on percentage visible. 204 | const { left } = this.containerEl.getBoundingClientRect(); 205 | const width = this.containerEl.clientWidth; 206 | const percentageVisible = (left + width) / width; 207 | endOpenedState = percentageVisible >= 0.5; 208 | } 209 | 210 | this.containerEl.style.transform = ""; 211 | this.opened = endOpenedState; 212 | 213 | document.body.removeEventListener("touchmove", this.onGestureMove); 214 | document.body.removeEventListener("touchend", this.onGestureEnd); 215 | document.body.removeEventListener("touchcancel", this.onGestureEnd); 216 | } 217 | 218 | updateFromTouch() { 219 | if (!this.__touching || !this.containerEl) { 220 | return; 221 | } 222 | requestAnimationFrame(this.updateFromTouch); 223 | 224 | const translateX = Math.min(0, this._currentX - this._startX + this._left); 225 | this.containerEl.style.transform = `translateX(${translateX}px)`; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/src/SdpExternalLinks.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html, css } from "@lion/core"; 2 | 3 | export class SdpExternalLinks extends LitElement { 4 | static get properties() { 5 | return { 6 | drawer: { 7 | type: Boolean, 8 | reflect: true, 9 | }, 10 | }; 11 | } 12 | 13 | static get styles() { 14 | return css` 15 | :host { 16 | display: flex; 17 | align-items: center; 18 | gap: 2rem; 19 | } 20 | 21 | .link { 22 | display: none; 23 | } 24 | 25 | .how { 26 | color: #11aea7; 27 | } 28 | 29 | .hamburger { 30 | display: block; 31 | } 32 | 33 | .github img, 34 | .discord img { 35 | display: block; 36 | } 37 | 38 | .github img { 39 | height: 30px; 40 | } 41 | 42 | .discord img { 43 | height: 25px; 44 | } 45 | 46 | @media (min-width: 601px) { 47 | :host { 48 | display: flex; 49 | } 50 | 51 | .hamburger { 52 | display: none; 53 | } 54 | 55 | .link { 56 | display: block; 57 | } 58 | } 59 | 60 | :host([drawer]) { 61 | display: flex; 62 | padding: 20px; 63 | flex-direction: column; 64 | background-color: rgb(217, 248, 245); 65 | } 66 | 67 | :host([drawer]) .link { 68 | display: block; 69 | } 70 | 71 | :host([drawer]) .hamburger { 72 | display: none; 73 | } 74 | 75 | .codicon[class*="codicon-"] { 76 | font: normal normal normal 26px/1 codicon; 77 | background-color: transparent; 78 | border: none; 79 | cursor: pointer; 80 | } 81 | 82 | .codicon-menu:before { 83 | content: "\\eb94"; 84 | } 85 | `; 86 | } 87 | 88 | render() { 89 | return html` 90 | ${this.drawer ? html` ` : ""} 91 | How it works 98 | discord 106 | github 114 | 118 | `; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/src/SdpLogo.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html, css } from "@lion/core"; 2 | 3 | export class SdpLogo extends LitElement { 4 | static get styles() { 5 | return css` 6 | :host a, 7 | :host a:link, 8 | :host a:visited, 9 | :host a:focus, 10 | :host a:hover, 11 | :host a:active { 12 | color: #2e2e46; 13 | } 14 | .logo-link { 15 | display: block; 16 | text-decoration: none; 17 | margin-bottom: 0; 18 | } 19 | 20 | .logo-link > div { 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .logo-link__img { 26 | width: 2rem; 27 | margin-right: 0.5rem; 28 | margin-top: 0.5rem; 29 | } 30 | 31 | .font-logo .big { 32 | font-size: 130%; 33 | } 34 | 35 | .font-logo .small { 36 | font-size: 70%; 37 | } 38 | 39 | .divriots-logo { 40 | display: inline-flex; 41 | align-items: flex-end; 42 | gap: 0.25rem; 43 | position: relative; 44 | left: 40px; 45 | top: -8px; 46 | fill: #2e2e46; 47 | text-decoration: none; 48 | } 49 | 50 | .divriots-logo img { 51 | height: 0.875rem; 52 | display: inline; 53 | margin-bottom: 1px; 54 | } 55 | `; 56 | } 57 | 58 | render() { 59 | return html` 60 | 61 |
62 | style-dictionary logo 67 | 71 |
72 |
73 | 82 | `; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/browser/sdp-nav/src/SdpNav.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html, css } from "@lion/core"; 2 | import "../sdp-drawer.js"; 3 | import "../sdp-external-links.js"; 4 | import "../sdp-logo.js"; 5 | 6 | export class SdpNav extends LitElement { 7 | static get styles() { 8 | return css` 9 | .nav-items { 10 | width: 100%; 11 | display: flex; 12 | justify-content: space-between; 13 | } 14 | `; 15 | } 16 | 17 | get hamburgerEl() { 18 | return this.shadowRoot.querySelector(".hamburger"); 19 | } 20 | 21 | constructor() { 22 | super(); 23 | } 24 | 25 | connectedCallback() { 26 | super.connectedCallback(); 27 | this.setAttribute("role", "navigation"); 28 | this.setAttribute("aria-label", "Main navigation"); 29 | } 30 | 31 | render() { 32 | return html` 33 | 43 | `; 44 | } 45 | 46 | hamburgerToggle() { 47 | this.shadowRoot.getElementById("drawer").toggle(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/card/card.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0 1rem; 5 | font-family: "Source Sans Pro", sans-serif; 6 | } 7 | 8 | .card { 9 | width: 100%; 10 | background-color: white; 11 | overflow: hidden; 12 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.6); 13 | border-radius: var(--sd-card-border-radius-mobile); 14 | } 15 | 16 | .card img { 17 | display: block; 18 | width: 100%; 19 | height: 200px; 20 | object-fit: cover; 21 | } 22 | 23 | .card h2, 24 | .card p { 25 | padding: 1rem; 26 | margin: 0; 27 | } 28 | 29 | .card h2 { 30 | color: var(--sd-card-heading-color); 31 | } 32 | 33 | .card p { 34 | color: var(--sd-card-text-color); 35 | } 36 | 37 | @media (min-width: 600px) { 38 | .card { 39 | border-radius: var(--sd-card-border-radius-desktop); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/card/card.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |

Chameleons are cool

16 |

They can change color 🤯

17 |

This is a component preview using tokens, with live updates!

18 |

19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /src/card/card.js: -------------------------------------------------------------------------------- 1 | let res; 2 | const loadComplete = new Promise((resolve) => { 3 | res = resolve; 4 | }); 5 | 6 | window.addEventListener("load", () => { 7 | res(); 8 | }); 9 | 10 | // Helper so we can pass CSS from style-dictionary output into this iframe 11 | // since adopted style sheets may not be shared across documents.. 12 | globalThis.insertCSS = async (cssText) => { 13 | const sheet = new CSSStyleSheet(); 14 | sheet.replaceSync(cssText); 15 | await loadComplete; 16 | document.adoptedStyleSheets = [sheet]; 17 | }; 18 | 19 | async function setViewportText() { 20 | await loadComplete; 21 | if (window.innerWidth < 600) { 22 | document.getElementById("viewport-text").innerText = "Viewport: Mobile"; 23 | } else { 24 | document.getElementById("viewport-text").innerText = "Viewport: Desktop"; 25 | } 26 | } 27 | 28 | window.addEventListener("resize", () => { 29 | setViewportText(); 30 | }); 31 | setViewportText(); 32 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divriots/style-dictionary-playground/50459309c60f519440d1b772758f1e6f729e522d/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 36 | 40 | 44 | 48 | Style Dictionary Playground 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 64 | 65 | 66 | 67 |
68 | 69 |
70 |
71 |

72 | Play with 73 | Style Dictionary 79 |

80 |

81 | Once you save any file, the URL encodes your progress so it's easy to 82 | share! 83 |

84 |
85 |
86 |
87 | 88 | 93 |
94 | 95 |
96 |
97 |

98 | Made for our Design System platform: 99 | Backlight.dev. Read the 100 | integration docs. 102 |

103 |
104 |

Card Component example

105 |

106 | Style Dictionary can output to lots of different formats, for example 107 | Web or iOS. 108 |

109 |

110 | Below is a card component which is based on the tokens. Try changing the 111 | card tokens! 112 |

113 |

114 | Note: if you're viewing this on small viewport below 740px, the frame 115 | below will be set to mobile. 116 |

117 |
118 |
119 | 120 | 121 |
122 | 123 |
124 |
125 |
126 |

Features

127 |
    128 |
  • 129 | Allows tokens or config as JS file for more advanced use cases like 130 | custom formats / transforms 131 |
  • 132 |
  • 133 | Supports relative imports between JS files, to reuse token partials 134 |
  • 135 |
  • 136 | Supports using StyleDictionary object by importing: 137 | import StyleDictionary 139 | from 140 | 'style-dictionary'; 142 | in the config. This is useful if you need for example formatHelpers 143 | (e.g. 144 | StyleDictionary.formatHelpers.fileHeader({ file })) 149 |
  • 150 |
  • 151 | FileTree supports keyboard navigation. F2 to edit currently focused 152 | file or folder. 153 |
  • 154 |
  • 155 | Can download all files in your current playground as a ZIP with the 156 | download button at the bottom left of the editor. 157 |
  • 158 |
  • 159 | Supports dispatching a MessageEvent using postMessage 160 | { type: 162 | 'sd-tokens-request' }, 164 | { type: 166 | 'sd-dictionary-request' } 168 | or 169 | { type: 171 | 'sd-input-files-request' } 173 | which will dispatch a MessageEvent to the source of the request with 174 | either the tokens, the current dictionary instance, or all of the 175 | input files and its contents. Useful for third-party integrations 176 | through iframes. Note that for postMessage cloning, dictionary 177 | instance is JSON.stringified as it contains functions. 178 |
  • 179 |
180 |

Third party integration

181 |

182 | You can load this app in an iframe with a project ID in the URL and then 183 | use 184 | postMessage 188 | to request all kinds of useful data from the style-dictionary-play 189 | instance, which the app will then send back with postMessage. Use '*' 190 | for origin. 191 |

192 |
    193 |
  • 194 | { type: 196 | 'sd-tokens-request' } 198 | for just the rudimentary tokens object 199 |
  • 200 |
  • 201 | { type: 203 | 'sd-dictionary-request' } 205 | for just the rudimentary dictionary object. This object will not 206 | contain any methods because those cannot be cloned and sent over 207 | postMessage. 208 |
  • 209 |
  • 210 | { type: 212 | 'sd-enriched-tokens-request' 213 | } 215 | for the full tokens and allTokens (flattened version of tokens) object 216 | after running transforms and resolutions based on a specific platform. 217 | This will give much more metadata about the tokens. You have to pass 218 | the platform as a string property in the request message. 219 | You can find which platforms are used by first sending an 220 | sd-dictionary-request which contains the configured platforms. 221 |
  • 222 |
  • 223 | { type: 225 | 'sd-input-files-request' } 227 | for all input files and their contents as strings. 228 |
  • 229 |
230 |

How it works

231 |

232 | For a full explanation, 233 | check out the blog we wrote about it. 239 |

240 |

241 | We use a 242 | browser patch 248 | of 249 | style-dictionary 255 | in combination with 256 | browserify 259 | and a shim for file-system (e.g. memfs or 260 | browserify-fs) to make the tool work in the browser. 261 |

262 |

263 | We then use monaco-editor and some file-tree utilities to make the 264 | playground editor work with it. 265 |

266 |

267 | The input files are encoded in the URL when you save files, making 268 | playground snippets easy to share with others! 269 |

270 |
271 | 311 | 312 | 313 | -------------------------------------------------------------------------------- /src/node/file-tree-utils.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import util from "util"; 3 | import path from "path"; 4 | import glob from "glob"; 5 | import { configPaths, changeLang, getContents } from "./index.js"; 6 | import { 7 | styleDictionaryInstance, 8 | styleDictionaryInstanceSet, 9 | rerunStyleDictionaryIfSourceChanged, 10 | } from "./run-style-dictionary.js"; 11 | import createDictionary from "browser-style-dictionary/lib/utils/createDictionary.js"; 12 | import mkdirRecursive from "./mkdirRecursive.js"; 13 | import { ensureMonacoIsLoaded, editor } from "../browser/monaco.js"; 14 | 15 | const asyncGlob = util.promisify(glob); 16 | const extensionMap = { 17 | js: "javascript", 18 | }; 19 | const tokensPath = path.resolve("tokens"); 20 | const fileTreeEl = document.querySelector("file-tree"); 21 | 22 | async function currentFileContentChanged() { 23 | const selectedFileBtn = getSelectedFileBtn(); 24 | if (selectedFileBtn) { 25 | selectedFileBtn.setAttribute("unsaved", ""); 26 | } 27 | } 28 | 29 | function getSelectedFileBtn() { 30 | return fileTreeEl.checkedFileBtn; 31 | } 32 | 33 | export async function createInputFiles() { 34 | const urlSplit = window.location.href.split("#project="); 35 | if (urlSplit.length > 1) { 36 | const encoded = urlSplit[1]; 37 | await new Promise((resolve) => setTimeout(resolve, 200)); 38 | const parsedContents = JSON.parse(flate.deflate_decode(encoded)); 39 | await Promise.all( 40 | Object.entries(parsedContents).map(async ([file, content]) => { 41 | return new Promise(async (resolve) => { 42 | const dir = path.dirname(file); 43 | if (dir !== "/") { 44 | await mkdirRecursive(dir); 45 | } 46 | fs.writeFile(file, content, (err) => { 47 | resolve(); 48 | }); 49 | }); 50 | }) 51 | ); 52 | } else { 53 | fs.mkdirSync(`color`); 54 | fs.mkdirSync(`card`); 55 | fs.mkdirSync(`radii`); 56 | 57 | fs.writeFileSync( 58 | // take the .json by default 59 | configPaths.find((pa) => pa.endsWith(".json")), 60 | JSON.stringify( 61 | { 62 | source: ["**/*.tokens.json"], 63 | platforms: { 64 | css: { 65 | transformGroup: "css", 66 | prefix: "sd", 67 | buildPath: "build/css/", 68 | files: [ 69 | { 70 | destination: "_variables.css", 71 | format: "css/variables", 72 | }, 73 | ], 74 | }, 75 | js: { 76 | transformGroup: "js", 77 | buildPath: "build/js/", 78 | files: [ 79 | { 80 | destination: "variables.js", 81 | format: "javascript/es6", 82 | }, 83 | ], 84 | }, 85 | }, 86 | }, 87 | null, 88 | 2 89 | ) 90 | ); 91 | 92 | fs.writeFileSync( 93 | path.join(`color`, "base.tokens.json"), 94 | JSON.stringify( 95 | { 96 | color: { 97 | base: { 98 | gray: { 99 | light: { value: "#CCCCCC" }, 100 | medium: { value: "#999999" }, 101 | dark: { value: "#111111" }, 102 | }, 103 | red: { value: "#FF0000" }, 104 | green: { value: "#00FF00" }, 105 | }, 106 | }, 107 | }, 108 | null, 109 | 2 110 | ) 111 | ); 112 | 113 | fs.writeFileSync( 114 | path.join(`color`, "font.tokens.json"), 115 | JSON.stringify( 116 | { 117 | color: { 118 | font: { 119 | base: { value: "{color.base.red}" }, 120 | secondary: { value: "{color.base.green}" }, 121 | tertiary: { value: "{color.base.gray.dark}" }, 122 | }, 123 | }, 124 | }, 125 | null, 126 | 2 127 | ) 128 | ); 129 | 130 | fs.writeFileSync( 131 | path.join(`card`, "card.tokens.json"), 132 | JSON.stringify( 133 | { 134 | card: { 135 | border: { 136 | radius: { 137 | mobile: { 138 | value: "{radii.none}", 139 | }, 140 | desktop: { 141 | value: "{radii.sm}", 142 | }, 143 | }, 144 | }, 145 | heading: { 146 | color: { 147 | value: "{color.font.base}", 148 | }, 149 | }, 150 | text: { 151 | color: { 152 | value: "{color.font.tertiary}", 153 | }, 154 | }, 155 | }, 156 | }, 157 | null, 158 | 2 159 | ) 160 | ); 161 | 162 | fs.writeFileSync( 163 | path.join(`radii`, "base.tokens.json"), 164 | JSON.stringify( 165 | { 166 | radii: { 167 | none: { 168 | value: "0", 169 | }, 170 | sm: { 171 | value: "8px", 172 | }, 173 | }, 174 | }, 175 | null, 176 | 2 177 | ) 178 | ); 179 | } 180 | } 181 | 182 | export async function createFile(filename) { 183 | await new Promise((resolve) => { 184 | fs.writeFile(filename, "", () => { 185 | resolve(); 186 | }); 187 | }); 188 | } 189 | 190 | export async function createFolder(foldername) { 191 | await new Promise((resolve) => { 192 | fs.mkdir(foldername, (err) => { 193 | resolve(); 194 | }); 195 | }); 196 | } 197 | 198 | export async function editFileName(filePath, newName, isFolder = false) { 199 | const newPath = path.join(path.dirname(filePath), newName); 200 | fs.renameSync(filePath, newPath); 201 | await rerunStyleDictionaryIfSourceChanged(newPath, isFolder); 202 | } 203 | 204 | export async function removeFile(file) { 205 | if (file.endsWith("/")) { 206 | await new Promise((resolve) => { 207 | fs.rmdir(file, { recursive: true }, () => { 208 | resolve(); 209 | }); 210 | }); 211 | } else { 212 | await new Promise((resolve) => { 213 | fs.unlink(file, () => { 214 | resolve(); 215 | }); 216 | }); 217 | } 218 | await repopulateFileTree(); 219 | } 220 | 221 | export async function openAllFolders() { 222 | await fileTreeEl.updateComplete; 223 | Array.from(fileTreeEl.shadowRoot.querySelectorAll("details")).forEach( 224 | (el) => { 225 | el.setAttribute("open", ""); 226 | } 227 | ); 228 | } 229 | 230 | export async function clearAll() { 231 | const files = await asyncGlob("**/*", { fs, mark: true }); 232 | const filtered = files.filter((file) => file !== "sd.config.json"); 233 | await Promise.all( 234 | filtered.map((file) => { 235 | return new Promise(async (resolve) => { 236 | if (file.endsWith("/")) { 237 | await new Promise((resolve) => { 238 | fs.rmdir(file, { recursive: true }, () => { 239 | resolve(); 240 | }); 241 | }); 242 | } else if (!file.match("/")) { 243 | await new Promise((resolve) => { 244 | fs.unlink(file, () => { 245 | resolve(); 246 | }); 247 | }); 248 | } 249 | resolve(); 250 | }); 251 | }) 252 | ); 253 | await repopulateFileTree(); 254 | } 255 | 256 | export async function saveCurrentFile() { 257 | const selectedFileBtn = getSelectedFileBtn(); 258 | if (!selectedFileBtn) { 259 | return; 260 | } 261 | const selectedFile = selectedFileBtn.getAttribute("full-path"); 262 | if (!selectedFile) { 263 | return; 264 | } 265 | await new Promise(async (resolve) => { 266 | await ensureMonacoIsLoaded(); 267 | fs.writeFile(selectedFile, editor.getValue(), () => { 268 | resolve(); 269 | }); 270 | }); 271 | selectedFileBtn.removeAttribute("unsaved"); 272 | await rerunStyleDictionaryIfSourceChanged(`/${selectedFile}`); 273 | } 274 | 275 | function openOrCloseJSSwitch(file) { 276 | const container = document.getElementById("jsSwitchContainer"); 277 | if (container.hasAttribute("closed-by-user")) { 278 | return; 279 | } 280 | if (configPaths.includes(`/${file}`) && file.endsWith(".json")) { 281 | container.style.display = "flex"; 282 | } else { 283 | container.style.display = "none"; 284 | } 285 | } 286 | 287 | export async function switchToFile(file) { 288 | openOrCloseJSSwitch(file); 289 | const ext = path.extname(file).slice(1); 290 | const lang = extensionMap[ext] || ext; 291 | const fileData = await new Promise((resolve) => { 292 | fs.readFile(file, "utf-8", (err, data) => { 293 | resolve(data); 294 | }); 295 | }); 296 | await ensureMonacoIsLoaded(); 297 | editor.setValue(fileData); 298 | await changeLang(lang); 299 | editor.setScrollTop(0); 300 | } 301 | 302 | export async function setupFileChangeHandlers() { 303 | await ensureMonacoIsLoaded(); 304 | editor.onDidChangeModelContent((ev) => { 305 | if (!ev.isFlush) { 306 | currentFileContentChanged(); 307 | } 308 | }); 309 | editor._domElement.addEventListener("keydown", (ev) => { 310 | if (ev.key === "s" && (ev.ctrlKey || ev.metaKey)) { 311 | ev.preventDefault(); 312 | saveCurrentFile(); 313 | } 314 | }); 315 | } 316 | 317 | export async function getAllFiles() { 318 | const filePaths = await asyncGlob("**/*", { fs, nodir: true }); 319 | 320 | const allFiles = {}; 321 | await Promise.all( 322 | filePaths.map((filePath) => { 323 | return new Promise(async (resolve) => { 324 | const content = await new Promise((resolve) => { 325 | fs.readFile(filePath, "utf-8", (err, data) => { 326 | resolve(data); 327 | }); 328 | }); 329 | allFiles[filePath] = content; 330 | resolve(); 331 | }); 332 | }) 333 | ); 334 | return allFiles; 335 | } 336 | 337 | export async function getInputFiles() { 338 | await styleDictionaryInstanceSet; 339 | const allFiles = await asyncGlob("**/*", { nodir: true, fs }); 340 | const outputFiles = await getOutputFiles(); 341 | return allFiles.filter((file) => !outputFiles.includes(file)); 342 | } 343 | 344 | export async function getOutputFiles() { 345 | // without a correct SD instance, we can't really know for sure what the output files are 346 | // therefore, we can't know what the input files are (tokens + other used files via relative imports) 347 | await styleDictionaryInstanceSet; 348 | const { platforms } = styleDictionaryInstance.options; 349 | let outputFiles = []; 350 | await Promise.all( 351 | Object.entries(platforms).map(([key, platform]) => { 352 | return new Promise(async (resolve) => { 353 | const outFiles = await asyncGlob(`${platform.buildPath}**`, { 354 | nodir: true, 355 | fs, 356 | }); 357 | outputFiles = [...outputFiles, ...outFiles]; 358 | resolve(); 359 | }); 360 | }) 361 | ); 362 | return outputFiles; 363 | } 364 | 365 | export async function repopulateFileTree() { 366 | if (!styleDictionaryInstance) { 367 | console.error( 368 | "Trying to repopulate file tree without a valid style-dictionary object to check which files are input vs output." 369 | ); 370 | } 371 | const inputFiles = await getInputFiles(); 372 | const outputFiles = await getOutputFiles(); 373 | fileTreeEl.outputFiles = outputFiles; 374 | fileTreeEl.inputFiles = inputFiles; 375 | } 376 | 377 | export async function dispatchTokens(ev) { 378 | const { source } = ev; 379 | await styleDictionaryInstanceSet; 380 | source.postMessage( 381 | { 382 | type: "sd-tokens", 383 | tokens: styleDictionaryInstance.tokens, 384 | }, 385 | "*" 386 | ); 387 | } 388 | 389 | export async function dispatchDictionary(ev) { 390 | const { source } = ev; 391 | await styleDictionaryInstanceSet; 392 | // Dictionary can contain methods, for postMessage cloning as a workaround 393 | // we therefore have to JSON.stringify it and JSON.parse it to clone which removes functions. 394 | const dictionary = JSON.parse(JSON.stringify(styleDictionaryInstance)); 395 | source.postMessage( 396 | { 397 | type: "sd-dictionary", 398 | dictionary, 399 | }, 400 | "*" 401 | ); 402 | } 403 | 404 | export async function dispatchEnrichedTokens(ev) { 405 | const { source, data } = ev; 406 | const { platform } = data; 407 | await styleDictionaryInstanceSet; 408 | const enrichedTokens = styleDictionaryInstance.exportPlatform(platform); 409 | const { allTokens, tokens } = createDictionary({ 410 | properties: enrichedTokens, 411 | }); 412 | source.postMessage({ type: "sd-enriched-tokens", tokens, allTokens }, "*"); 413 | } 414 | 415 | export async function dispatchInputFiles(ev) { 416 | const { source } = ev; 417 | const inputFiles = await getInputFiles(); 418 | const contents = await getContents(inputFiles); 419 | source.postMessage({ type: "sd-input-files", files: contents }, "*"); 420 | } 421 | -------------------------------------------------------------------------------- /src/node/index.js: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | import prettier from "prettier"; 4 | import babel from "@babel/parser"; 5 | import { 6 | createInputFiles, 7 | setupFileChangeHandlers, 8 | dispatchTokens, 9 | dispatchInputFiles, 10 | dispatchDictionary, 11 | dispatchEnrichedTokens, 12 | openAllFolders, 13 | } from "./file-tree-utils.js"; 14 | import runStyleDictionary, { 15 | findUsedConfigPath, 16 | rerunStyleDictionaryIfSourceChanged, 17 | } from "./run-style-dictionary.js"; 18 | import { ensureMonacoIsLoaded, editor, monaco } from "../browser/monaco.js"; 19 | import "../browser/index.js"; 20 | 21 | // supported config paths, prioritized in this order 22 | export const configPaths = [ 23 | "config.js", 24 | "sd.config.js", 25 | "config.mjs", 26 | "sd.config.mjs", 27 | "config.json", 28 | "sd.config.json", 29 | ].map((p) => path.resolve(p)); 30 | 31 | export async function changeLang(lang) { 32 | await ensureMonacoIsLoaded(); 33 | monaco.editor.setModelLanguage(editor.getModel(), lang); 34 | } 35 | 36 | export async function getContents(files) { 37 | const contents = {}; 38 | await Promise.all( 39 | files.map(async (file) => { 40 | await new Promise((resolve) => { 41 | fs.readFile(file, "utf-8", (err, data) => { 42 | contents[file] = data; 43 | resolve(); 44 | }); 45 | }); 46 | }) 47 | ); 48 | return contents; 49 | } 50 | 51 | export async function encodeContents(files) { 52 | const contents = await getContents(files); 53 | const content = JSON.stringify(contents); 54 | return flate.deflate_encode(content); 55 | } 56 | 57 | async function switchToJS(ev) { 58 | const configPath = findUsedConfigPath(); 59 | if (configPath.endsWith(".json")) { 60 | ev.target.parentElement.style.display = "none"; 61 | const contents = fs.readFileSync(configPath, "utf-8"); 62 | const newPath = `${configPath.split(".json")[0]}.js`; 63 | const newContents = prettier.format(`export default ${contents};`, { 64 | // explicitly use babel parser, just parser: "babel" will not work, 65 | // rollup won't be smart enough to understand to put babel parser in 66 | // final bundle like that because prettier will try to find and use it 67 | // under the hood (using its own resolution logic??) 68 | parser: (text) => babel.parse(text, { sourceType: "module" }), 69 | }); 70 | fs.unlinkSync(configPath); 71 | fs.writeFileSync(newPath, newContents, "utf-8"); 72 | await rerunStyleDictionaryIfSourceChanged(newPath); 73 | await document.querySelector("file-tree").switchToFile(newPath); 74 | } 75 | } 76 | 77 | function switchClose(ev) { 78 | ev.target.parentElement.style.display = "none"; 79 | ev.target.parentElement.setAttribute("closed-by-user", ""); 80 | } 81 | 82 | (async function () { 83 | window.addEventListener("message", (ev) => { 84 | const { data } = ev; 85 | switch (data.type) { 86 | case "sd-tokens-request": 87 | dispatchTokens(ev); 88 | break; 89 | case "sd-input-files-request": 90 | dispatchInputFiles(ev); 91 | break; 92 | case "sd-dictionary-request": 93 | dispatchDictionary(ev); 94 | break; 95 | case "sd-enriched-tokens-request": 96 | dispatchEnrichedTokens(ev); 97 | break; 98 | } 99 | }); 100 | await createInputFiles(); 101 | await runStyleDictionary(); 102 | await openAllFolders(); 103 | await setupFileChangeHandlers(); 104 | window.addEventListener("resize", async () => { 105 | await ensureMonacoIsLoaded(); 106 | editor.layout({}); 107 | editor.layout(); 108 | }); 109 | document.getElementById("jsSwitchBtn").addEventListener("click", switchToJS); 110 | document 111 | .getElementById("jsSwitchClose") 112 | .addEventListener("click", switchClose); 113 | await ensureMonacoIsLoaded(); 114 | editor.layout({}); 115 | editor.layout(); 116 | })(); 117 | -------------------------------------------------------------------------------- /src/node/mkdirRecursive.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | export default async function mkdirRecursive(pathToCreate) { 5 | await pathToCreate.split(path.sep).reduce(async (prevPath, folder) => { 6 | const resolvedPrevPath = await prevPath; 7 | const currentPath = path.join(resolvedPrevPath, folder, path.sep); 8 | const exists = await new Promise((resolve) => { 9 | fs.stat(currentPath, (err, stats) => { 10 | resolve(!Boolean(err)); 11 | }); 12 | }); 13 | if (!exists) { 14 | await new Promise((resolve) => { 15 | fs.mkdir(currentPath, () => { 16 | resolve(); 17 | }); 18 | }); 19 | } 20 | return currentPath; 21 | }, ""); 22 | } 23 | -------------------------------------------------------------------------------- /src/node/run-style-dictionary.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import { v4 as uuidv4 } from "uuid"; 4 | import * as rollup from "rollup"; 5 | import StyleDictionary from "browser-style-dictionary/browser.js"; 6 | import { repopulateFileTree, getInputFiles } from "./file-tree-utils.js"; 7 | import { configPaths, encodeContents } from "./index.js"; 8 | 9 | export let styleDictionaryInstance; 10 | let sdInstanceSetResolve; 11 | export const styleDictionaryInstanceSet = new Promise((resolve) => { 12 | sdInstanceSetResolve = resolve; 13 | }); 14 | 15 | async function cleanPlatformOutputDirs() { 16 | if (!styleDictionaryInstance || !styleDictionaryInstance.platforms) { 17 | return; 18 | } 19 | const foldersToClean = new Set(); 20 | Object.entries(styleDictionaryInstance.platforms).map(([key, val]) => { 21 | foldersToClean.add(val.buildPath.split("/")[0]); 22 | }); 23 | 24 | await Promise.all( 25 | Array.from(foldersToClean).map((folder) => { 26 | return new Promise((resolve) => { 27 | fs.rmdir(folder, { recursive: true }, () => { 28 | resolve(); 29 | }); 30 | }); 31 | }) 32 | ); 33 | } 34 | 35 | // If we don't have the CSS props or card tokens 36 | // there's no use in showing the card component demo 37 | function getCSSText(filePath) { 38 | if (!fs.existsSync(filePath)) { 39 | document.querySelector(".card-container").style.display = "none"; 40 | return ""; 41 | } 42 | const cssProps = fs.readFileSync(filePath, "utf-8"); 43 | if (cssProps.match(/--sd-card-/g)) { 44 | document.querySelector(".card-container").style.display = "block"; 45 | } else { 46 | document.querySelector(".card-container").style.display = "none"; 47 | } 48 | return cssProps; 49 | } 50 | 51 | function exportCSSPropsToCardFrame() { 52 | const cssProps = getCSSText("build/css/_variables.css"); 53 | if (!cssProps) { 54 | return; 55 | } 56 | 57 | const cardFrame = document.getElementById("card-frame"); 58 | // if iframe is not fully loaded we can't inject the CSS sheet yet 59 | if (cardFrame.contentWindow.document.readyState !== "complete") { 60 | cardFrame.contentWindow.addEventListener("load", () => { 61 | cardFrame.contentWindow.requestAnimationFrame(() => { 62 | cardFrame?.contentWindow.insertCSS(cssProps); 63 | }); 64 | }); 65 | return; 66 | } 67 | 68 | const insertCSS = async (cssProps) => { 69 | try { 70 | cardFrame?.contentWindow.insertCSS(cssProps); 71 | } catch (e) { 72 | // If insertCSS is not available on iframe window yet, try again after 100ms 73 | await new Promise((resolve) => setTimeout(resolve, 100)); 74 | insertCSS(cssProps); 75 | } 76 | }; 77 | insertCSS(cssProps); 78 | } 79 | 80 | export async function rerunStyleDictionaryIfSourceChanged( 81 | file, 82 | isFolder = false 83 | ) { 84 | const previousRunError = !styleDictionaryInstance; 85 | 86 | // If previous run was okay, check whether we need a new run 87 | if (!previousRunError) { 88 | const inputFiles = await getInputFiles(); 89 | const isInputFile = inputFiles.includes(file.replace(/^\//, "")); 90 | // Only run style dictionary if the config or input files were changed 91 | if (!isInputFile && !isFolder) { 92 | return; 93 | } 94 | } 95 | await runStyleDictionary(); 96 | 97 | const inputFiles = await getInputFiles(); 98 | // If no inputFiles, run was error so can't send something useful to analytics atm or encode contents in url 99 | if (inputFiles.length > 0) { 100 | // We use fathom for analytics, here we track dictionary runs 101 | window.fathom.trackGoal("XBWJBW1W", 0); 102 | const encoded = await encodeContents(inputFiles); 103 | window.location.href = `${window.location.origin}/#project=${encoded}`; 104 | } 105 | } 106 | 107 | export function findUsedConfigPath() { 108 | return configPaths.find((cfgPath) => fs.existsSync(cfgPath)); 109 | } 110 | 111 | /** 112 | * Somewhat naive bundle step with rollup 113 | * This will allow relative import specifiers inside the playground 114 | * Might be nice for JS tokens importing/exporting rather than using 115 | * the SD {} reference syntax that you can only use inside "value"s 116 | * 117 | * EXAMPLE: 118 | * 119 | * import foo from '../foo/bar.js'; 120 | * 121 | * export default { 122 | * "color": { 123 | * ...foo, 124 | * } 125 | * } 126 | */ 127 | async function bundle(inputPath) { 128 | const sdName = uuidv4(); 129 | globalThis[sdName] = StyleDictionary; 130 | const rollupCfg = await rollup.rollup({ 131 | input: inputPath, 132 | plugins: [ 133 | { 134 | name: "resolve-bare-esm-run", 135 | async resolveId(id) { 136 | // if id is not relative or absolute or style-dictionary -> bare import to resolve from esm.run 137 | if (!id.match(/^(\/|\.).+$/g) && id !== "style-dictionary") { 138 | return { id: `https://esm.run/${id}`, external: true }; 139 | } 140 | return null; 141 | }, 142 | }, 143 | { 144 | name: "fake-import", 145 | resolveId(source, importer) { 146 | let resolved; 147 | if (source === inputPath) { 148 | resolved = inputPath; 149 | } else if (importer) { 150 | // try to resolve it from our virtual FS 151 | resolved = path.resolve(path.dirname(importer), source); 152 | } 153 | return resolved; 154 | }, 155 | load(id) { 156 | if (id) { 157 | // try to load it from our virtual FS 158 | return fs.readFileSync(id, "utf-8"); 159 | } 160 | }, 161 | }, 162 | { 163 | name: "sd-external", 164 | // Naive and simplified regex version of rollup externals global plugin just for style-dictionary import.. 165 | transform(code) { 166 | let rewrittenCode = code; 167 | let matchRes = rewrittenCode.match( 168 | /import (?.+?) from 'style-dictionary';/, 169 | "" 170 | ); 171 | if (matchRes) { 172 | let { id } = matchRes.groups; 173 | // Remove the import statement, replace the id wherever used with the global 174 | rewrittenCode = rewrittenCode 175 | .replace(matchRes[0], "") 176 | .replace(new RegExp(id, "g"), `globalThis['${sdName}']`); 177 | } 178 | return rewrittenCode; 179 | }, 180 | }, 181 | ], 182 | }); 183 | const bundle = await rollupCfg.generate({ format: "es" }); 184 | return bundle.output[0].code; 185 | } 186 | 187 | export default async function runStyleDictionary() { 188 | console.log("Running style-dictionary..."); 189 | document.querySelector("file-tree").animateCue(); 190 | await cleanPlatformOutputDirs(); 191 | let cfgObj; 192 | const configPath = findUsedConfigPath(); 193 | let newStyleDictionary = {}; 194 | try { 195 | // If .js, we need to parse it as actual JS without resorting to eval/Function 196 | // Instead, we put it in a blob and create a URL from it that we can import 197 | // That way, malicious code would be scoped only to the blob, which is safer. 198 | if (configPath.match(/\.(j|mj)s$/)) { 199 | const bundled = await bundle(configPath); 200 | const url = URL.createObjectURL( 201 | new Blob([bundled], { type: "text/javascript" }) 202 | ); 203 | const { default: cfg } = await import(url); 204 | cfgObj = cfg; 205 | } else { 206 | cfgObj = JSON.parse(fs.readFileSync(configPath, "utf-8")); 207 | } 208 | 209 | // Custom parser for JS files 210 | cfgObj.parsers = [ 211 | ...(cfgObj.parsers || []), 212 | { 213 | // matches js, mjs 214 | pattern: /\.(j|mj)s$/, 215 | parse: async ({ filePath }) => { 216 | const bundled = await bundle(filePath); 217 | const url = URL.createObjectURL( 218 | new Blob([bundled], { type: "text/javascript" }) 219 | ); 220 | const { default: token } = await import(url); 221 | return token; 222 | }, 223 | }, 224 | ]; 225 | 226 | newStyleDictionary = await StyleDictionary.extend(cfgObj); 227 | styleDictionaryInstance = newStyleDictionary; 228 | sdInstanceSetResolve(); 229 | await newStyleDictionary.buildAllPlatforms(); 230 | exportCSSPropsToCardFrame(); 231 | } catch (e) { 232 | console.error(`Style Dictionary error: ${e.stack}`); 233 | } finally { 234 | await repopulateFileTree(); 235 | return newStyleDictionary; 236 | } 237 | } 238 | --------------------------------------------------------------------------------