From 6708615c48ec1bf25b4e51667ec3eaef422a6cd9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 1 Mar 2021 00:25:31 +0530 Subject: [PATCH 01/16] Load data from gists --- package.json | 1 + src/components/Editor.svelte | 14 +++- src/gist-utils.js | 29 ++++++++ yarn.lock | 127 +++++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/gist-utils.js diff --git a/package.json b/package.json index b978a259ce..e7bbc32e31 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "pretty": "prettier --write ./src/*" }, "dependencies": { + "@octokit/rest": "^18.3.0", "js-base64": "^2.5.1", "json-lint": "^0.1.0", "jsonlint": "^1.6.3", diff --git a/src/components/Editor.svelte b/src/components/Editor.svelte index 3d96acdb4d..5d10df27c6 100644 --- a/src/components/Editor.svelte +++ b/src/components/Editor.svelte @@ -1,5 +1,5 @@ diff --git a/src/gist-utils.js b/src/gist-utils.js new file mode 100644 index 0000000000..74b23622bf --- /dev/null +++ b/src/gist-utils.js @@ -0,0 +1,29 @@ +import { Octokit } from '@octokit/rest'; + +const octokit = new Octokit({ + userAgent: 'mermaid-live-editor', + baseUrl: 'https://api.github.com', +}); + +const isValidGist = (files) => { + return 'config.json' in files && 'mermaid.diagram' in files; +}; + +const getFileContent = async (file) => { + if (file.truncated) { + return await (await fetch(file.raw_url)).text(); + } + return file.content; +}; + +export const getMermaidData = async (gist_id) => { + const gist = await octokit.gists.get({ + gist_id, + }); + const files = gist.data.files; + if (isValidGist(files)) { + const config = await getFileContent(files['config.json']); + const code = await getFileContent(files['mermaid.diagram']); + return { config, code }; + } +}; diff --git a/yarn.lock b/yarn.lock index e1d05296c8..e31f839da1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32,6 +32,108 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== +"@octokit/auth-token@^2.4.4": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3" + integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA== + dependencies: + "@octokit/types" "^6.0.3" + +"@octokit/core@^3.2.3": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.5.tgz#57becbd5fd789b0592b915840855f3a5f233d554" + integrity sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.4.12" + "@octokit/types" "^6.0.3" + before-after-hook "^2.1.0" + universal-user-agent "^6.0.0" + +"@octokit/endpoint@^6.0.1": + version "6.0.11" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1" + integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ== + dependencies: + "@octokit/types" "^6.0.3" + is-plain-object "^5.0.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^4.5.8": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.0.tgz#f9abca55f82183964a33439d5264674c701c3327" + integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^6.0.3" + universal-user-agent "^6.0.0" + +"@octokit/openapi-types@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.2.0.tgz#54e6ca0bc2cd54cd93f0a64658e893c32a5e69ec" + integrity sha512-MInMij2VK5o96Ei6qaHjxBglSZWOXQs9dTZfnGX2Xnr2mhA+yk9L/QCH4RcJGISJJCBclLHuY3ytq+iRgDfX7w== + +"@octokit/plugin-paginate-rest@^2.6.2": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.11.0.tgz#3568c43896a3355f4a0bbb3a64f443b2abdc760d" + integrity sha512-7L9xQank2G3r1dGqrVPo1z62V5utbykOUzlmNHPz87Pww/JpZQ9KyG5CHtUzgmB4n5iDRKYNK/86A8D98HP0yA== + dependencies: + "@octokit/types" "^6.11.0" + +"@octokit/plugin-request-log@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d" + integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ== + +"@octokit/plugin-rest-endpoint-methods@4.13.0": + version "4.13.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.0.tgz#59a90f59a564d416214ab191c3ffcd641f175e6a" + integrity sha512-Ofusy7BwHkU7z4TNsVdf7wm5W3KR625KqlQj4AiWPnBvclmZU0Y2bVK8b8Mz8nW7sEX9TJcCdX6KeaincE/cLw== + dependencies: + "@octokit/types" "^6.11.0" + deprecation "^2.3.1" + +"@octokit/request-error@^2.0.0": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" + integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== + dependencies: + "@octokit/types" "^6.0.3" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.3.0", "@octokit/request@^5.4.12": + version "5.4.14" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.14.tgz#ec5f96f78333bb2af390afa5ff66f114b063bc96" + integrity sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^6.7.1" + deprecation "^2.0.0" + is-plain-object "^5.0.0" + node-fetch "^2.6.1" + once "^1.4.0" + universal-user-agent "^6.0.0" + +"@octokit/rest@^18.3.0": + version "18.3.0" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.3.0.tgz#c326475a2039ffeaec9a1498ebee14a561aae4e5" + integrity sha512-R45oBVhnq3HAOGVtC6lHY7LX7TGWqbbcD4KvBHoT4QIjgJzfqKag3m/DUJwLnp8xrokz1spZmspTIXiDeQqJSA== + dependencies: + "@octokit/core" "^3.2.3" + "@octokit/plugin-paginate-rest" "^2.6.2" + "@octokit/plugin-request-log" "^1.0.2" + "@octokit/plugin-rest-endpoint-methods" "4.13.0" + +"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.7.1": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.11.0.tgz#830a608882cde659be19a6e86568abaa619e2ee7" + integrity sha512-RMLAmpPZf/a33EsclBazKg02NCCj4rC69V9sUgf0SuWTjmnBD2QC1nIVtJo7RJrGnwG1+aoFBb2yTrWm/8AS7w== + dependencies: + "@octokit/openapi-types" "^5.2.0" + "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -478,6 +580,11 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= +before-after-hook@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.1.tgz#99ae36992b5cfab4a83f6bee74ab27835f28f405" + integrity sha512-5ekuQOvO04MDj7kYZJaMab2S8SPjGJbotVNyv7QYFCOAwrGZs/YnoDNlh1U+m5hl7H2D/+n0taaAV/tfyd3KMA== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -1501,6 +1608,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +deprecation@^2.0.0, deprecation@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -2636,6 +2748,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-regex@^1.0.4: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" @@ -3256,6 +3373,11 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" @@ -4870,6 +4992,11 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" +universal-user-agent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" + integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" From 32351ab24e111f2423b2189d8115ff540b9a90d3 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 02:25:12 +0530 Subject: [PATCH 02/16] Load data from urls --- .eslintrc.cjs | 10 ++- package.json | 135 ++++++++++++++--------------- src/gist-utils.js | 29 ------- src/lib/util/fileLoaders/gist.ts | 29 +++++++ src/lib/util/fileLoaders/loader.ts | 29 +++++++ src/lib/util/util.ts | 2 + tsconfig.json | 2 +- 7 files changed, 137 insertions(+), 99 deletions(-) delete mode 100644 src/gist-utils.js create mode 100644 src/lib/util/fileLoaders/gist.ts create mode 100644 src/lib/util/fileLoaders/loader.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 5de3a147e1..75890d3edd 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -8,7 +8,15 @@ module.exports = { 'prettier' ], plugins: ['svelte3', 'tailwindcss', '@typescript-eslint'], - ignorePatterns: ['docs/*', '*.cjs', '*.md', 'snapshots.js', 'svelte.config.js', 'package.json'], + ignorePatterns: [ + 'docs/*', + '*.cjs', + '*.md', + 'snapshots.js', + 'svelte.config.js', + 'package.json', + 'tsconfig.json' + ], overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], settings: { 'svelte3/typescript': () => require('typescript') diff --git a/package.json b/package.json index 68fa4b2936..2f4589ebaf 100644 --- a/package.json +++ b/package.json @@ -1,69 +1,68 @@ { - "name": "mermaid-live-editor", - "version": "2.0.67", - "scripts": { - "dev": "svelte-kit dev", - "build": "svelte-kit build", - "preview": "svelte-kit preview", - "lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", - "lint:fix": "prettier --write --plugin-search-dir=. . && eslint --fix --ignore-path .gitignore .", - "format": "prettier --write --plugin-search-dir=. .", - "pre-commit": "lint-staged", - "postinstall": "husky install" - }, - "devDependencies": { - "@cypress/snapshot": "^2.1.7", - "@sveltejs/adapter-static": "1.0.0-next.11", - "@sveltejs/kit": "^1.0.0-next.109", - "@types/mermaid": "^8.2.6", - "@typescript-eslint/eslint-plugin": "^4.26.0", - "@typescript-eslint/parser": "^4.26.0", - "autoprefixer": "^10.2.6", - "chai": "^4.3.4", - "cssnano": "^5.0.5", - "cypress": "^7.4.0", - "cypress-localstorage-commands": "^1.4.5", - "eslint": "^7.27.0", - "eslint-config-prettier": "^8.1.0", - "eslint-plugin-cypress": "^2.11.3", - "eslint-plugin-mocha": "^9.0.0", - "eslint-plugin-postcss-modules": "^1.2.1", - "eslint-plugin-svelte3": "^3.2.0", - "eslint-plugin-tailwindcss": "^1.9.1", - "husky": "^6.0.0", - "lint-staged": "^11.0.0", - "mocha": "^8.4.0", - "postcss": "^8.3.0", - "postcss-load-config": "^3.0.1", - "prettier": "~2.3.0", - "prettier-plugin-svelte": "^2.3.0", - "svelte": "^3.34.0", - "svelte-preprocess": "^4.7.1", - "tailwindcss": "^2.1.4", - "tslib": "^2.0.0", - "typescript": "^4.3.2" - }, - "type": "module", - "dependencies": { - "@octokit/rest": "^18.3.0", - "@analytics/google-analytics": "^0.5.3", - "@macfja/svelte-persistent-store": "^1.1.1", - "analytics": "^0.7.5", - "js-base64": "^3.6.1", - "mermaid": "^8.10.1", - "moment": "^2.29.1", - "monaco-editor": "^0.24.0", - "random-word-slugs": "^0.0.2" - }, - "lint-staged": { - "*.{ts,svelte,js,css,md,json}": [ - "prettier --plugin-search-dir=. --write", - "eslint --ignore-path .gitignore " - ] - }, - "volta": { - "node": "14.16.1", - "yarn": "1.22.10", - "npm": "7.11.2" - } -} \ No newline at end of file + "name": "mermaid-live-editor", + "version": "2.0.67", + "scripts": { + "dev": "svelte-kit dev", + "build": "svelte-kit build", + "preview": "svelte-kit preview", + "lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", + "lint:fix": "prettier --write --plugin-search-dir=. . && eslint --fix --ignore-path .gitignore .", + "format": "prettier --write --plugin-search-dir=. .", + "pre-commit": "lint-staged", + "postinstall": "husky install" + }, + "devDependencies": { + "@cypress/snapshot": "^2.1.7", + "@sveltejs/adapter-static": "1.0.0-next.11", + "@sveltejs/kit": "^1.0.0-next.109", + "@types/mermaid": "^8.2.6", + "@typescript-eslint/eslint-plugin": "^4.26.0", + "@typescript-eslint/parser": "^4.26.0", + "autoprefixer": "^10.2.6", + "chai": "^4.3.4", + "cssnano": "^5.0.5", + "cypress": "^7.4.0", + "cypress-localstorage-commands": "^1.4.5", + "eslint": "^7.27.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-cypress": "^2.11.3", + "eslint-plugin-mocha": "^9.0.0", + "eslint-plugin-postcss-modules": "^1.2.1", + "eslint-plugin-svelte3": "^3.2.0", + "eslint-plugin-tailwindcss": "^1.9.1", + "husky": "^6.0.0", + "lint-staged": "^11.0.0", + "mocha": "^8.4.0", + "postcss": "^8.3.0", + "postcss-load-config": "^3.0.1", + "prettier": "~2.3.0", + "prettier-plugin-svelte": "^2.3.0", + "svelte": "^3.34.0", + "svelte-preprocess": "^4.7.1", + "tailwindcss": "^2.1.4", + "tslib": "^2.0.0", + "typescript": "^4.3.2" + }, + "type": "module", + "dependencies": { + "@analytics/google-analytics": "^0.5.3", + "@macfja/svelte-persistent-store": "^1.1.1", + "analytics": "^0.7.5", + "js-base64": "^3.6.1", + "mermaid": "^8.10.1", + "moment": "^2.29.1", + "monaco-editor": "^0.24.0", + "random-word-slugs": "^0.0.2" + }, + "lint-staged": { + "*.{ts,svelte,js,css,md,json}": [ + "prettier --plugin-search-dir=. --write", + "eslint --ignore-path .gitignore " + ] + }, + "volta": { + "node": "14.16.1", + "yarn": "1.22.10", + "npm": "7.11.2" + } +} diff --git a/src/gist-utils.js b/src/gist-utils.js deleted file mode 100644 index 74b23622bf..0000000000 --- a/src/gist-utils.js +++ /dev/null @@ -1,29 +0,0 @@ -import { Octokit } from '@octokit/rest'; - -const octokit = new Octokit({ - userAgent: 'mermaid-live-editor', - baseUrl: 'https://api.github.com', -}); - -const isValidGist = (files) => { - return 'config.json' in files && 'mermaid.diagram' in files; -}; - -const getFileContent = async (file) => { - if (file.truncated) { - return await (await fetch(file.raw_url)).text(); - } - return file.content; -}; - -export const getMermaidData = async (gist_id) => { - const gist = await octokit.gists.get({ - gist_id, - }); - const files = gist.data.files; - if (isValidGist(files)) { - const config = await getFileContent(files['config.json']); - const code = await getFileContent(files['mermaid.diagram']); - return { config, code }; - } -}; diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts new file mode 100644 index 0000000000..57a354e767 --- /dev/null +++ b/src/lib/util/fileLoaders/gist.ts @@ -0,0 +1,29 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +const isValidGist = (files: any): boolean => { + return 'diagram.mmd' in files; +}; + +const getFileContent = async (file: any): Promise => { + if (file.truncated) { + return await (await fetch(file.raw_url)).text(); + } + return file.content; +}; + +export const getGistData = async (gistURL: string): Promise<{ code: string; config?: string }> => { + const gistID = gistURL.split('/').pop(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { files } = await (await fetch(`https://api.github.com/gists/${gistID}`)).json(); + + if (isValidGist(files)) { + const code = await getFileContent(files['diagram.mmd']); + if (!('config.json' in files)) { + return { code }; + } + const config = await getFileContent(files['config.json']); + return { config, code }; + } else { + throw 'Invalid gist provided'; + } +}; diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts new file mode 100644 index 0000000000..24144d2929 --- /dev/null +++ b/src/lib/util/fileLoaders/loader.ts @@ -0,0 +1,29 @@ +import { getGistData } from './gist'; +import { updateCode, updateConfig } from '../state'; +type MermaidData = { code: string; config?: string }; +type Loader = (url: string) => Promise; +const loaders: Record = { + gist: getGistData +}; + +export const loadDataFromUrl = async (): Promise => { + const searchParams = new URLSearchParams(window.location.search); + let code: string, config: string; + if (searchParams.has('code')) { + code = await (await fetch(searchParams.get('code'))).text(); + } + if (searchParams.has('config')) { + config = await (await fetch(searchParams.get('config'))).text(); + } + + if (!code) { + for (const [key, value] of searchParams.entries()) { + if (key in loaders) { + ({ code, config } = await loaders[key](value)); + break; + } + } + } + code && updateCode(code, true, true); + config && updateConfig(config, true); +}; diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts index 482dab1cfd..1536ff3a8a 100644 --- a/src/lib/util/util.ts +++ b/src/lib/util/util.ts @@ -1,6 +1,7 @@ import type { State } from '$lib/types'; import { initURLSubscription, loadState, updateCodeStore } from './state'; import { analytics, initAnalytics } from './stats'; +import { loadDataFromUrl } from './fileLoaders/loader'; export const loadStateFromURL = (): void => { loadState(window.location.hash.slice(1)); @@ -14,6 +15,7 @@ export const syncDiagram = (): void => { export const initHandler = async (): Promise => { loadStateFromURL(); + await loadDataFromUrl(); syncDiagram(); initURLSubscription(); await initAnalytics(); diff --git a/tsconfig.json b/tsconfig.json index 975f193c02..e82fd2974c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "moduleResolution": "node", "module": "es2020", - "lib": ["es2020", "ESNext"], + "lib": ["es2020", "ESNext", "DOM.Iterable"], "target": "es2019", /** svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript From f2cf3e8111513bbe11d8ac358e7c25c3f642a7b8 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 02:27:42 +0530 Subject: [PATCH 03/16] Fix types --- src/lib/types.d.ts | 3 +++ src/lib/util/fileLoaders/gist.ts | 3 ++- src/lib/util/fileLoaders/loader.ts | 3 +-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts index 3f121d9337..0a393d08d6 100644 --- a/src/lib/types.d.ts +++ b/src/lib/types.d.ts @@ -37,3 +37,6 @@ export interface HistoryEntry { name?: string; auto: boolean; } + +type MermaidData = { code: string; config?: string }; +type Loader = (url: string) => Promise; diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index 57a354e767..7459b34233 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ +import type { MermaidData } from '$lib/types'; const isValidGist = (files: any): boolean => { return 'diagram.mmd' in files; }; @@ -11,7 +12,7 @@ const getFileContent = async (file: any): Promise => { return file.content; }; -export const getGistData = async (gistURL: string): Promise<{ code: string; config?: string }> => { +export const getGistData = async (gistURL: string): Promise => { const gistID = gistURL.split('/').pop(); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const { files } = await (await fetch(`https://api.github.com/gists/${gistID}`)).json(); diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index 24144d2929..1771c1f5b9 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -1,7 +1,6 @@ import { getGistData } from './gist'; import { updateCode, updateConfig } from '../state'; -type MermaidData = { code: string; config?: string }; -type Loader = (url: string) => Promise; +import type { Loader } from '$lib/types'; const loaders: Record = { gist: getGistData }; From aef1afe3f4ec1d8bab66d88b66b7068f0cc237f2 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 02:48:06 +0530 Subject: [PATCH 04/16] Update file names --- src/lib/util/fileLoaders/gist.ts | 12 ++++++++---- src/lib/util/fileLoaders/loader.ts | 1 - src/lib/util/util.ts | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index 7459b34233..05bdfc188f 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -1,8 +1,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { MermaidData } from '$lib/types'; + +const codeFileName = 'diagram.mmd'; +const configFileName = 'config.json'; + const isValidGist = (files: any): boolean => { - return 'diagram.mmd' in files; + return codeFileName in files; }; const getFileContent = async (file: any): Promise => { @@ -18,11 +22,11 @@ export const getGistData = async (gistURL: string): Promise => { const { files } = await (await fetch(`https://api.github.com/gists/${gistID}`)).json(); if (isValidGist(files)) { - const code = await getFileContent(files['diagram.mmd']); - if (!('config.json' in files)) { + const code = await getFileContent(files[codeFileName]); + if (!(configFileName in files)) { return { code }; } - const config = await getFileContent(files['config.json']); + const config = await getFileContent(files[configFileName]); return { config, code }; } else { throw 'Invalid gist provided'; diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index 1771c1f5b9..f4d0b08b82 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -14,7 +14,6 @@ export const loadDataFromUrl = async (): Promise => { if (searchParams.has('config')) { config = await (await fetch(searchParams.get('config'))).text(); } - if (!code) { for (const [key, value] of searchParams.entries()) { if (key in loaders) { diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts index 1536ff3a8a..f244f9ffe9 100644 --- a/src/lib/util/util.ts +++ b/src/lib/util/util.ts @@ -15,7 +15,7 @@ export const syncDiagram = (): void => { export const initHandler = async (): Promise => { loadStateFromURL(); - await loadDataFromUrl(); + await loadDataFromUrl().catch(console.error); syncDiagram(); initURLSubscription(); await initAnalytics(); From f5878feaa535ca3843212d589d619f377d501b2e Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 02:53:35 +0530 Subject: [PATCH 05/16] Fix file name --- src/lib/util/fileLoaders/gist.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index 05bdfc188f..bbcefea0f2 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { MermaidData } from '$lib/types'; -const codeFileName = 'diagram.mmd'; +const codeFileName = 'code.mmd'; const configFileName = 'config.json'; const isValidGist = (files: any): boolean => { From fdff260f7f07955d72728560cdd277246bc7b524 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 11:52:13 +0530 Subject: [PATCH 06/16] Show git revision history --- cypress/integration/loadSite.spec.ts | 14 ++++ cypress/snapshots.js | 23 ++++--- src/lib/components/card/tabs.svelte | 2 +- src/lib/components/history/history.svelte | 65 ++++++++++++------ src/lib/components/history/history.ts | 38 ++++++++--- src/lib/types.d.ts | 20 +++++- src/lib/util/fileLoaders/gist.ts | 81 ++++++++++++++++++++--- src/lib/util/fileLoaders/loader.ts | 43 +++++++++--- src/lib/util/state.ts | 2 +- 9 files changed, 223 insertions(+), 65 deletions(-) diff --git a/cypress/integration/loadSite.spec.ts b/cypress/integration/loadSite.spec.ts index 0c58d3735b..1d49cc518a 100644 --- a/cypress/integration/loadSite.spec.ts +++ b/cypress/integration/loadSite.spec.ts @@ -29,4 +29,18 @@ describe('Site Loads', () => { cy.contains('Class Diagram').click(); cy.contains('classDiagram'); }); + + it('should load diagram from gist', () => { + cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`); + cy.contains('Party'); + cy.getLocalStorage('codeStore').snapshot(); + }); + + it('should load diagram from raw files', () => { + cy.visit( + `/edit?code=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/a76fc7c6d19b6b772d86ac87fa700ed2e2117c56/code.mmd&config=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/a76fc7c6d19b6b772d86ac87fa700ed2e2117c56/config.json` + ); + cy.contains('Party'); + cy.getLocalStorage('codeStore').snapshot(); + }); }); diff --git a/cypress/snapshots.js b/cypress/snapshots.js index f94d5e4a97..53fec1c2fd 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -1,13 +1,4 @@ module.exports = { - "Site Loads": { - "Check Home page load": { - "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}" - }, - "Check Redirect from old URL": { - "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}" - } - }, - "__version": "7.3.0", "Auto sync tests": { "should dim diagram when code is edited": { "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}" @@ -15,5 +6,17 @@ module.exports = { "should not dim diagram when code is in sync": { "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}" } - } + }, + "Site Loads": { + "Check Home page load": { + "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}" + }, + "Check Redirect from old URL": { + "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" + }, + "should load diagram from gist": { + "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" + } + }, + "__version": "7.4.0" } diff --git a/src/lib/components/card/tabs.svelte b/src/lib/components/card/tabs.svelte index fc11b338ec..dc479b6fb7 100644 --- a/src/lib/components/card/tabs.svelte +++ b/src/lib/components/card/tabs.svelte @@ -7,7 +7,7 @@ export let title: string; export let isOpen = false; - let activeTabID: string = tabs[0]?.id; + $: activeTabID = tabs[0]?.id; const dispatch = createEventDispatcher(); const toggleTabs = (tab: Tab) => { diff --git a/src/lib/components/history/history.svelte b/src/lib/components/history/history.svelte index 1010d40dc0..96350cfda2 100644 --- a/src/lib/components/history/history.svelte +++ b/src/lib/components/history/history.svelte @@ -3,29 +3,30 @@ import { codeStore, getStateString } from '$lib/util/state'; import { addHistoryEntry, - autoHistoryMode, + historyModeStore, clearHistoryData, getPreviousState, - historyStore + historyStore, + loaderHistoryStore } from './history'; import { notify, prompt } from '$lib/util/notify'; import { onMount } from 'svelte'; import moment from 'moment'; - import type { State, Tab } from '$lib/types'; + import type { HistoryType, State, Tab } from '$lib/types'; const HISTORY_SAVE_INTERVAL = 60000; const tabSelectHandler = (message: CustomEvent) => { - autoHistoryMode.set('timeline' === message.detail.id); + historyModeStore.set(message.detail.id as HistoryType); }; - const tabs: Tab[] = [ + let tabs: Tab[] = [ { - id: 'saved', + id: 'manual', title: 'Saved', icon: 'far fa-bookmark' }, { - id: 'timeline', + id: 'auto', title: 'Timeline', icon: 'fas fa-history' } @@ -38,7 +39,7 @@ addHistoryEntry({ state: $codeStore, time: Date.now(), - auto + type: auto ? 'auto' : 'manual' }); } else if (!auto) { notify('State already saved.'); @@ -62,12 +63,26 @@ }; onMount(() => { - autoHistoryMode.set(false); + historyModeStore.set('manual'); setInterval(() => { saveHistory(true); }, HISTORY_SAVE_INTERVAL); }); + loaderHistoryStore.subscribe((entries) => { + if (entries.length > 0 && tabs.length === 2) { + tabs = [ + { + id: 'loader', + title: 'Revisions', + icon: 'fab fa-git-alt' + }, + ...tabs + ]; + historyModeStore.set('loader'); + } + }); + let isOpen = true; @@ -78,20 +93,30 @@ class="btn" on:click|stopPropagation={() => saveHistory()} title="Save current state"> - + {#if $historyModeStore !== 'loader'} + + {/if}
    {#if $historyStore.length > 0} - {#each $historyStore as { state, time, name }} + {#each $historyStore as { state, time, name, url, type }}
  • - {name} + {#if url} + {name} + {:else} + {name} + {/if} {relativeTime(time)}
    @@ -99,9 +124,11 @@ - + {#if type !== 'loader'} + + {/if}
  • diff --git a/src/lib/components/history/history.ts b/src/lib/components/history/history.ts index 4b7677a681..8dc0809b7d 100644 --- a/src/lib/components/history/history.ts +++ b/src/lib/components/history/history.ts @@ -1,12 +1,12 @@ import { derived, Readable, Writable, writable, get } from 'svelte/store'; import { persist, localStorage } from '@macfja/svelte-persistent-store'; import { generateSlug } from 'random-word-slugs'; -import type { HistoryEntry } from '../../types'; +import type { HistoryEntry, HistoryType } from '$lib/types'; const MAX_AUTO_HISTORY_LENGTH = 30; -export const autoHistoryMode: Writable = persist( - writable(true), +export const historyModeStore: Writable = persist( + writable('manual'), localStorage(), 'autoHistoryMode' ); @@ -23,17 +23,30 @@ const manualHistoryStore: Writable = persist( 'manualHistoryStore' ); +export const loaderHistoryStore: Writable = writable([] as HistoryEntry[]); + export const historyStore: Readable = derived( - [autoHistoryMode, autoHistoryStore, manualHistoryStore], - ([autoMode, autoHistories, manualHistories], set) => { - set(autoMode ? autoHistories : manualHistories); + [historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore], + ([historyMode, autoHistories, manualHistories, loadedHistories], set) => { + if (historyMode === 'auto') { + set(autoHistories); + } else if (historyMode === 'manual') { + set(manualHistories); + } else if (historyMode === 'loader') { + set(loadedHistories); + } else { + set(autoHistories); + } } ); export const addHistoryEntry = (entry: HistoryEntry): void => { + if (entry.type === 'loader') { + loaderHistoryStore.update((entries) => [entry, ...entries]); + return; + } entry.name = generateSlug(2); - - if (!entry.auto) { + if (entry.type !== 'auto') { manualHistoryStore.update((entries) => [entry, ...entries]); return; } @@ -46,9 +59,12 @@ export const addHistoryEntry = (entry: HistoryEntry): void => { }; export const clearHistoryData = (time?: number): void => { - (get(autoHistoryMode) ? autoHistoryStore : manualHistoryStore).update((entries) => - entries.filter((entry) => time && entry.time != time) - ); + (get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => { + if (get(historyModeStore) !== 'loader') { + entries = entries.filter((entry) => time && entry.time != time); + } + return entries; + }); }; export const getPreviousState = (auto: boolean): string => { diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts index 0a393d08d6..3969b14d00 100644 --- a/src/lib/types.d.ts +++ b/src/lib/types.d.ts @@ -29,14 +29,28 @@ export interface State { updateEditor: boolean; updateDiagram: boolean; autoSync: boolean; + loader?: LoaderConfig; } +export interface GistLoaderConfig { + url: string; +} + +export interface FileLoaderConfig { + codeURL: string; + configURL?: string; +} +export interface LoaderConfig { + type: 'gist' | 'files'; + config: GistLoaderConfig | FileLoaderConfig; +} +export type HistoryType = 'auto' | 'manual' | 'loader'; export interface HistoryEntry { state: State; time: number; name?: string; - auto: boolean; + type: HistoryType; + url?: string; } -type MermaidData = { code: string; config?: string }; -type Loader = (url: string) => Promise; +type Loader = (url: string) => Promise; diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index bbcefea0f2..2a9968f6cc 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -1,6 +1,9 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { MermaidData } from '$lib/types'; +import type { State } from '$lib/types'; +import { defaultState } from '../state'; +import { addHistoryEntry } from '../../components/history/history'; const codeFileName = 'code.mmd'; const configFileName = 'config.json'; @@ -16,19 +19,79 @@ const getFileContent = async (file: any): Promise => { return file.content; }; -export const getGistData = async (gistURL: string): Promise => { - const gistID = gistURL.split('/').pop(); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const { files } = await (await fetch(`https://api.github.com/gists/${gistID}`)).json(); +interface GistData { + code: string; + config?: string; + author: string; + time: number; + version: string; + url: string; +} +const getGistData = async (gistURL: string): Promise => { + const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { html_url, files, history } = await ( + await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`) + ).json(); if (isValidGist(files)) { const code = await getFileContent(files[codeFileName]); - if (!(configFileName in files)) { - return { code }; + let config: string; + if (configFileName in files) { + config = await getFileContent(files[configFileName]); } - const config = await getFileContent(files[configFileName]); - return { config, code }; + const currentItem = history[0]; + return { + url: `${html_url}/${currentItem.version}`, + code, + config, + author: currentItem.user.login, + time: new Date(currentItem.committed_at).getTime(), + version: (currentItem.version as string).slice(-7) + }; } else { throw 'Invalid gist provided'; } }; + +const getStateFromGist = (gist: GistData): State => { + const state = { + ...defaultState, + code: gist.code + }; + gist.config && (state.mermaid = gist.config); + return state; +}; + +export const loadGistData = async (gistURL: string): Promise => { + try { + const [_, __, gistID, revisionID] = gistURL.split('github.com').pop().split('/'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const { history } = await ( + await fetch(`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`) + ).json(); + const gistHistory: GistData[] = []; + for (const entry of history) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const data: GistData = await getGistData(entry.url).catch(() => undefined); + data && gistHistory.push(data); + } + if (gistHistory.length === 0) { + throw 'Invalid gist provided'; + } + gistHistory.reverse(); + const state = getStateFromGist(gistHistory.slice(-1).pop()); + for (const gist of gistHistory) { + addHistoryEntry({ + state: getStateFromGist(gist), + time: gist.time, + type: 'loader', + url: gist.url, + name: `${gist.author}-${gist.version}` + }); + } + return state; + } catch (err) { + console.error(err); + } +}; diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index f4d0b08b82..5a04b2a77f 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -1,27 +1,48 @@ -import { getGistData } from './gist'; -import { updateCode, updateConfig } from '../state'; -import type { Loader } from '$lib/types'; +import { loadGistData } from './gist'; +import { updateCodeStore, defaultState } from '../state'; +import type { Loader, State } from '$lib/types'; const loaders: Record = { - gist: getGistData + gist: loadGistData }; export const loadDataFromUrl = async (): Promise => { const searchParams = new URLSearchParams(window.location.search); + let state: State; let code: string, config: string; - if (searchParams.has('code')) { - code = await (await fetch(searchParams.get('code'))).text(); + const codeURL: string = searchParams.get('code'); + const configURL: string = searchParams.get('config'); + + if (codeURL) { + code = await (await fetch(codeURL)).text(); } - if (searchParams.has('config')) { - config = await (await fetch(searchParams.get('config'))).text(); + if (configURL) { + config = await (await fetch(configURL)).text(); + } else { + config = defaultState.mermaid; } if (!code) { for (const [key, value] of searchParams.entries()) { if (key in loaders) { - ({ code, config } = await loaders[key](value)); + state = await loaders[key](value); break; } } + } else { + state = { + code, + mermaid: config, + autoSync: true, + updateDiagram: true, + updateEditor: true, + loader: { + type: 'files', + config: { + codeURL, + configURL + } + } + }; } - code && updateCode(code, true, true); - config && updateConfig(config, true); + updateCodeStore(state); + // window.location.search = ''; }; diff --git a/src/lib/util/state.ts b/src/lib/util/state.ts index ebb839d5c1..d3c3280783 100644 --- a/src/lib/util/state.ts +++ b/src/lib/util/state.ts @@ -4,7 +4,7 @@ import { persist, localStorage } from '@macfja/svelte-persistent-store'; import type { State } from '$lib/types'; import { saveStatistcs } from './stats'; -const defaultState: State = { +export const defaultState: State = { code: `graph TD A[Christmas] -->|Get money| B(Go shopping) B --> C{Let me think} From ed2057312a8f0f5b585bbfef3b3d25da58672ef2 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 11:57:39 +0530 Subject: [PATCH 07/16] Fix test --- cypress/integration/loadSite.spec.ts | 2 +- cypress/snapshots.js | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cypress/integration/loadSite.spec.ts b/cypress/integration/loadSite.spec.ts index 1d49cc518a..1920ba2d99 100644 --- a/cypress/integration/loadSite.spec.ts +++ b/cypress/integration/loadSite.spec.ts @@ -38,7 +38,7 @@ describe('Site Loads', () => { it('should load diagram from raw files', () => { cy.visit( - `/edit?code=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/a76fc7c6d19b6b772d86ac87fa700ed2e2117c56/code.mmd&config=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/a76fc7c6d19b6b772d86ac87fa700ed2e2117c56/config.json` + '/edit?code=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd&config=https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json' ); cy.contains('Party'); cy.getLocalStorage('codeStore').snapshot(); diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 53fec1c2fd..39090b23f4 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -16,6 +16,9 @@ module.exports = { }, "should load diagram from gist": { "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" + }, + "should load diagram from raw files": { + "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}" } }, "__version": "7.4.0" From 43bac28b3063af4cbe2663a117171e5ab36f9459 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 12:13:19 +0530 Subject: [PATCH 08/16] Format names --- src/lib/util/fileLoaders/gist.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index 2a9968f6cc..6153b4b7c2 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -87,7 +87,7 @@ export const loadGistData = async (gistURL: string): Promise => { time: gist.time, type: 'loader', url: gist.url, - name: `${gist.author}-${gist.version}` + name: `${gist.author} v${gist.version}` }); } return state; From aed37c19c768be5853b0838b5b954980b59652b7 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 12:18:59 +0530 Subject: [PATCH 09/16] More tests --- cypress/integration/loadSite.spec.ts | 13 +++++++++++++ cypress/snapshots.js | 3 +++ 2 files changed, 16 insertions(+) diff --git a/cypress/integration/loadSite.spec.ts b/cypress/integration/loadSite.spec.ts index 1920ba2d99..b1de3c0a08 100644 --- a/cypress/integration/loadSite.spec.ts +++ b/cypress/integration/loadSite.spec.ts @@ -32,7 +32,20 @@ describe('Site Loads', () => { it('should load diagram from gist', () => { cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`); + cy.contains('Go shopping!!'); + cy.contains('Revisions'); + cy.contains('sidharthv96 v8f8f1e2'); + cy.contains('sidharthv96 v7851e19'); + cy.getLocalStorage('codeStore').snapshot(); + }); + + it('should load diagram from gist revision', () => { + cy.visit( + '/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19' + ); cy.contains('Party'); + cy.contains('Revisions'); + cy.contains('sidharthv96 v7851e19'); cy.getLocalStorage('codeStore').snapshot(); }); diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 39090b23f4..9d2f8a3326 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -19,6 +19,9 @@ module.exports = { }, "should load diagram from raw files": { "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}" + }, + "should load diagram from gist revision": { + "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" } }, "__version": "7.4.0" From cc3f4fabc2b49579f74d5d75dcb5d80ef1904d40 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 15:31:22 +0530 Subject: [PATCH 10/16] Loading Gist --- src/app.postcss | 4 +++ src/lib/components/actions.svelte | 43 +++++++++++++++++++++++++----- src/lib/types.d.ts | 4 +++ src/lib/util/fileLoaders/gist.ts | 12 +++++++-- src/lib/util/fileLoaders/loader.ts | 10 ++++--- src/lib/util/loading.ts | 19 +++++++++++++ src/lib/util/util.ts | 3 ++- src/routes/__layout.svelte | 42 +++++++++++++++++++++++++++++ 8 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 src/lib/util/loading.ts diff --git a/src/app.postcss b/src/app.postcss index ce6711bbcb..edf491bfa3 100644 --- a/src/app.postcss +++ b/src/app.postcss @@ -12,3 +12,7 @@ .action-btn { @apply rounded p-2 bg-indigo-400 shadow flex-auto text-white hover:bg-indigo-500; } + +.input { + @apply flex-1 border-indigo-400 border-solid border-2 rounded; +} diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index a119c4d5a3..ef3c03b440 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -1,7 +1,8 @@ - +
    {#if isClipboardAvailable()} + +
    + +
    + +
    diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts index 3969b14d00..e0a139b3cc 100644 --- a/src/lib/types.d.ts +++ b/src/lib/types.d.ts @@ -36,6 +36,10 @@ export interface GistLoaderConfig { url: string; } +export interface LoadingState { + loading: boolean; + message?: string; +} export interface FileLoaderConfig { codeURL: string; configURL?: string; diff --git a/src/lib/util/fileLoaders/gist.ts b/src/lib/util/fileLoaders/gist.ts index 6153b4b7c2..880032d9a8 100644 --- a/src/lib/util/fileLoaders/gist.ts +++ b/src/lib/util/fileLoaders/gist.ts @@ -55,9 +55,15 @@ const getGistData = async (gistURL: string): Promise => { }; const getStateFromGist = (gist: GistData): State => { - const state = { + const state: State = { ...defaultState, - code: gist.code + code: gist.code, + loader: { + type: 'gist', + config: { + url: gist.url + } + } }; gist.config && (state.mermaid = gist.config); return state; @@ -81,6 +87,8 @@ export const loadGistData = async (gistURL: string): Promise => { } gistHistory.reverse(); const state = getStateFromGist(gistHistory.slice(-1).pop()); + // @ts-ignore Ignore + state.loader.config.url = gistURL; for (const gist of gistHistory) { addHistoryEntry({ state: getStateFromGist(gist), diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index 5a04b2a77f..c9a0418414 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -7,7 +7,7 @@ const loaders: Record = { export const loadDataFromUrl = async (): Promise => { const searchParams = new URLSearchParams(window.location.search); - let state: State; + let state: State = defaultState; let code: string, config: string; const codeURL: string = searchParams.get('code'); const configURL: string = searchParams.get('config'); @@ -23,8 +23,12 @@ export const loadDataFromUrl = async (): Promise => { if (!code) { for (const [key, value] of searchParams.entries()) { if (key in loaders) { - state = await loaders[key](value); - break; + try { + state = await loaders[key](value); + break; + } catch (err) { + console.error(err); + } } } } else { diff --git a/src/lib/util/loading.ts b/src/lib/util/loading.ts new file mode 100644 index 0000000000..84205ebfca --- /dev/null +++ b/src/lib/util/loading.ts @@ -0,0 +1,19 @@ +import { writable, Writable } from 'svelte/store'; +import type { LoadingState } from '$lib/types'; + +const defaultLoading: LoadingState = { + loading: false +}; + +export const loadingStateStore: Writable = writable(defaultLoading); +export const initLoading = async (message: string, task: Promise): Promise => { + loadingStateStore.set({ + loading: true, + message + }); + const result: T = await task; + loadingStateStore.set({ + loading: false + }); + return result; +}; diff --git a/src/lib/util/util.ts b/src/lib/util/util.ts index f244f9ffe9..84e02992c0 100644 --- a/src/lib/util/util.ts +++ b/src/lib/util/util.ts @@ -2,6 +2,7 @@ import type { State } from '$lib/types'; import { initURLSubscription, loadState, updateCodeStore } from './state'; import { analytics, initAnalytics } from './stats'; import { loadDataFromUrl } from './fileLoaders/loader'; +import { initLoading } from './loading'; export const loadStateFromURL = (): void => { loadState(window.location.hash.slice(1)); @@ -15,7 +16,7 @@ export const syncDiagram = (): void => { export const initHandler = async (): Promise => { loadStateFromURL(); - await loadDataFromUrl().catch(console.error); + await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error)); syncDiagram(); initURLSubscription(); await initAnalytics(); diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index f17b2f6ea4..92cf899650 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -2,6 +2,7 @@ import '../app.postcss'; import { base } from '$app/paths'; import { onMount } from 'svelte'; + import { loadingStateStore } from '$lib/util/loading'; // This can be removed once https://github.com/sveltejs/kit/issues/1612 is fixed. // Then move it into src and vite will bundle it automatically. onMount(() => { @@ -23,3 +24,44 @@
    + +{#if $loadingStateStore.loading} +
    +
    +
    +
    {$loadingStateStore.message}
    +
    +
    +{/if} + + From 5961c7d4513f2e529112b7c897dd550e92600b94 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 15:44:26 +0530 Subject: [PATCH 11/16] Fix loading issue --- src/lib/util/fileLoaders/loader.ts | 5 ++++- src/routes/edit.svelte | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index c9a0418414..9207b5769c 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -9,11 +9,13 @@ export const loadDataFromUrl = async (): Promise => { const searchParams = new URLSearchParams(window.location.search); let state: State = defaultState; let code: string, config: string; + let loaded = false; const codeURL: string = searchParams.get('code'); const configURL: string = searchParams.get('config'); if (codeURL) { code = await (await fetch(codeURL)).text(); + loaded = true; } if (configURL) { config = await (await fetch(configURL)).text(); @@ -25,6 +27,7 @@ export const loadDataFromUrl = async (): Promise => { if (key in loaders) { try { state = await loaders[key](value); + loaded = true; break; } catch (err) { console.error(err); @@ -47,6 +50,6 @@ export const loadDataFromUrl = async (): Promise => { } }; } - updateCodeStore(state); + loaded && updateCodeStore(state); // window.location.search = ''; }; diff --git a/src/routes/edit.svelte b/src/routes/edit.svelte index 8f065c1050..3332c266bf 100644 --- a/src/routes/edit.svelte +++ b/src/routes/edit.svelte @@ -15,7 +15,7 @@ import type { EditorUpdateEvent, State, Tab } from '$lib/types'; import { base } from '$app/paths'; - const mermaid: Mermaid = (window.mermaid as unknown) as Mermaid; + const mermaid: Mermaid = window.mermaid as unknown as Mermaid; let selectedMode = 'code'; const languageMap = { From bf8160e31eb96b2e1f5324fb443525eb7a1506f2 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 15:55:59 +0530 Subject: [PATCH 12/16] Fix gist loading and code not updating issue --- cypress/integration/actions.spec.ts | 6 ++++++ src/lib/components/actions.svelte | 4 ++-- src/lib/util/fileLoaders/loader.ts | 13 ++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cypress/integration/actions.spec.ts b/cypress/integration/actions.spec.ts index 6a2a826852..bcef5906a7 100644 --- a/cypress/integration/actions.spec.ts +++ b/cypress/integration/actions.spec.ts @@ -17,4 +17,10 @@ describe('Check actions', () => { }); }); }); + + it('should load gists from URL', () => { + cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a'); + cy.contains('Load Gist').click(); + cy.contains('Go shopping!!'); + }); }); diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index ef3c03b440..438fd56661 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -121,8 +121,8 @@ if (!gistURL) { alert('Please enter a Gist URL first'); } - goto(`/edit?gist=${gistURL}`); - location.reload(); + window.location.href = `/edit?gist=${gistURL}`; + // location.reload(); }; let iUrl: string; diff --git a/src/lib/util/fileLoaders/loader.ts b/src/lib/util/fileLoaders/loader.ts index 9207b5769c..02b73aa2f5 100644 --- a/src/lib/util/fileLoaders/loader.ts +++ b/src/lib/util/fileLoaders/loader.ts @@ -38,9 +38,6 @@ export const loadDataFromUrl = async (): Promise => { state = { code, mermaid: config, - autoSync: true, - updateDiagram: true, - updateEditor: true, loader: { type: 'files', config: { @@ -48,8 +45,14 @@ export const loadDataFromUrl = async (): Promise => { configURL } } - }; + } as State; } - loaded && updateCodeStore(state); + loaded && + updateCodeStore({ + ...state, + autoSync: true, + updateDiagram: true, + updateEditor: true + }); // window.location.search = ''; }; From 103f02bb9075cd3ee7bd6613f7364144959c800d Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 5 Jun 2021 16:26:37 +0530 Subject: [PATCH 13/16] Fix tests --- cypress/snapshots.js | 26 +++++++++++++------------- src/lib/components/actions.svelte | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cypress/snapshots.js b/cypress/snapshots.js index 9d2f8a3326..deefeaa04b 100644 --- a/cypress/snapshots.js +++ b/cypress/snapshots.js @@ -1,12 +1,5 @@ module.exports = { - "Auto sync tests": { - "should dim diagram when code is edited": { - "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}" - }, - "should not dim diagram when code is in sync": { - "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}" - } - }, + "__version": "7.4.0", "Site Loads": { "Check Home page load": { "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}" @@ -15,14 +8,21 @@ module.exports = { "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" }, "should load diagram from gist": { - "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" + "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}" + }, + "should load diagram from gist revision": { + "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}" }, "should load diagram from raw files": { "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}" - }, - "should load diagram from gist revision": { - "1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true}" } }, - "__version": "7.4.0" + "Auto sync tests": { + "should dim diagram when code is edited": { + "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}" + }, + "should not dim diagram when code is in sync": { + "1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}" + } + } } diff --git a/src/lib/components/actions.svelte b/src/lib/components/actions.svelte index 438fd56661..212c813217 100644 --- a/src/lib/components/actions.svelte +++ b/src/lib/components/actions.svelte @@ -143,7 +143,7 @@ }); - +
    {#if isClipboardAvailable()}