Compare commits

..
Author SHA1 Message Date
Sidharth VinodandGitHub cc2b574a6f Merge branch 'develop' into sidv/fixErrorDiagram 2024-06-16 20:09:53 +05:30
Sidharth VinodandGitHub 71f4a9bac7 Merge pull request #1461 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-06-16 13:46:44 +00:00
renovate[bot]andGitHub 915b8990ae chore(deps): update all non-major dependencies 2024-06-16 13:46:29 +00:00
Sidharth VinodandGitHub 262d9e972a Merge pull request #1462 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-06-16 13:44:16 +00:00
Sidharth VinodandGitHub 77aa7d851a Merge pull request #1463 from mermaid-js/browserslist-update
build: update Browserslist db
2024-06-16 13:44:04 +00:00
Sidharth VinodandGitHub 6fbc5da142 Merge pull request #1412 from maxstanley/vendor-fa
Vendor Font-Awesome
2024-06-16 13:43:49 +00:00
renovate[bot]andGitHub 238557b911 chore(deps): update all non-major dependencies 2024-06-16 13:42:33 +00:00
Sidharth Vinod 81cc401bf2 Remove stalebot 2024-06-16 19:11:26 +05:30
Sidharth Vinod 7ce93ea210 Merge branch 'develop' into pr/maxstanley/1412
* develop: (34 commits)
  chore: Add isRough to stats
  Update snapshots
  feat: Add rough mode
  fix(deps): update dependency mermaid to v10.9.1
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  chore(deps): update dependency svelte to v4.2.16
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  build: update Browserslist db
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  Update README.md
  fix(#1401): Handle error when loading gist
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  Fix clicking issue in diagramUpdate.spec.ts
  Remove pnpm-lock.yaml
  ...
2024-06-16 19:07:48 +05:30
GitHub Action a09f5ee8b6 build: update Browserslist db 2024-06-15 02:24:59 +00:00
GitHub Action fa5bc5a5a1 build: update Browserslist db 2024-06-01 02:27:24 +00:00
Sidharth VinodandGitHub ca03a85221 Merge pull request #1457 from mermaid-js/sidv/rough
feat: Add rough mode
2024-05-23 12:55:33 +00:00
Sidharth Vinod 4525e0467c chore: Add isRough to stats 2024-05-23 18:25:13 +05:30
Sidharth Vinod 20b4ecc260 Update snapshots 2024-05-23 18:19:42 +05:30
Sidharth Vinod cba9a6754d feat: Add rough mode 2024-05-23 14:05:59 +05:30
Sidharth VinodandGitHub eb3b2d5446 Merge pull request #1450 from mermaid-js/browserslist-update
build: update Browserslist db
2024-05-21 06:52:10 +00:00
Sidharth VinodandGitHub 1e5248250d Merge pull request #1455 from mermaid-js/renovate/patch-mermaid-packages
fix(deps): update dependency mermaid to v10.9.1
2024-05-21 06:52:02 +00:00
renovate[bot]andGitHub b51ab519c2 fix(deps): update dependency mermaid to v10.9.1 2024-05-21 06:45:58 +00:00
Sidharth VinodandGitHub c91434346b Merge pull request #1454 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-05-21 06:44:54 +00:00
renovate[bot]andGitHub c9db1b90c4 chore(deps): update all non-major dependencies 2024-05-20 00:12:40 +00:00
GitHub Action 7b80de3683 build: update Browserslist db 2024-05-15 02:25:09 +00:00
Sidharth VinodandGitHub 116a84ea8d Merge pull request #1447 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update dependency svelte to v4.2.16
2024-05-13 05:42:19 +00:00
Sidharth VinodandGitHub 34a6d3c7b2 Merge pull request #1434 from kerwin612/patch-1
Update README.md
2024-05-13 11:12:02 +05:30
renovate[bot]andGitHub 386a19776b chore(deps): update dependency svelte to v4.2.16 2024-05-13 00:10:36 +00:00
Sidharth VinodandGitHub 528d2f0afe Merge pull request #1444 from mermaid-js/browserslist-update
build: update Browserslist db
2024-05-12 09:10:06 +05:30
Sidharth VinodandGitHub 8e7758a7be Merge pull request #1445 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-05-12 09:09:19 +05:30
renovate[bot]andGitHub 61a9ecf702 chore(deps): update all non-major dependencies 2024-05-10 01:52:24 +00:00
Sidharth VinodandGitHub dc72838036 Merge pull request #1442 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-05-03 07:03:47 +00:00
Sidharth VinodandGitHub 86a7909e2b Merge branch 'develop' into renovate/all-minor-patch 2024-05-03 12:33:35 +05:30
Sidharth VinodandGitHub 3dad6ebbb9 Merge pull request #1439 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-05-03 12:33:22 +05:30
renovate[bot]andGitHub 580e2f7506 chore(deps): update all non-major dependencies 2024-05-02 09:48:11 +00:00
renovate[bot]andGitHub cc227ea993 chore(deps): update all non-major dependencies 2024-05-01 15:30:24 +00:00
GitHub Action dcff346abd build: update Browserslist db 2024-05-01 02:23:10 +00:00
Sidharth VinodandGitHub 39e0293156 Merge pull request #1440 from mermaid-js/browserslist-update
build: update Browserslist db
2024-04-16 09:40:17 +05:30
GitHub Action 93b75efe71 build: update Browserslist db 2024-04-15 04:05:03 +00:00
Sidharth VinodandGitHub 68157f7e71 Merge pull request #1431 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-04-13 06:30:11 +00:00
renovate[bot]andGitHub 4eb23185a8 chore(deps): update all non-major dependencies 2024-04-13 06:29:55 +00:00
Sidharth VinodandGitHub 6d5a9acdc7 Merge pull request #1432 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-04-13 06:28:49 +00:00
Sidharth VinodandGitHub f862d29791 Merge pull request #1424 from mermaid-js/fix/1423
fix: subscripts in corner safari, resolves #1423
2024-04-13 06:28:41 +00:00
renovate[bot]andGitHub c5d3eb322a chore(deps): update all non-major dependencies 2024-04-11 19:52:59 +00:00
Kerwin BryantandGitHub df66e433bf Update README.md 2024-04-09 14:00:30 +08:00
Sidharth VinodandGitHub 29b9188d88 Merge branch 'develop' into fix/1423 2024-04-05 19:07:12 +05:30
Sidharth VinodandGitHub 0e6c022a0f Merge pull request #1429 from mermaid-js/1401_LoadGist
fix(#1401): Handle error when loading gist
2024-04-05 19:06:56 +05:30
Sidharth Vinod 57eef9da5c fix(#1401): Handle error when loading gist 2024-04-05 18:58:02 +05:30
Sidharth VinodandGitHub e635b23694 Merge pull request #1421 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-04-03 00:42:08 +05:30
Sidharth VinodandGitHub 36390c2034 Merge pull request #1425 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-04-03 00:41:53 +05:30
renovate[bot]andGitHub d2f5dd148f chore(deps): update all non-major dependencies 2024-03-31 02:17:36 +00:00
renovate[bot]andGitHub bc9e8c306b chore(deps): update all non-major dependencies 2024-03-29 10:19:32 +00:00
Sidharth VinodandGitHub 618ac917ec Merge pull request #1383 from dontry/feature/zenuml
Add @mermaid-js/mermaid-zenuml dependency and ZenUML sample diagram
2024-03-27 14:46:05 +05:30
dontry 329839a9a7 Fix clicking issue in diagramUpdate.spec.ts 2024-03-24 21:45:42 +11:00
Sidharth Vinod cfd8484f76 Remove pnpm-lock.yaml 2024-03-24 15:01:24 +05:30
DonandGitHub 6175a02d5a Merge branch 'mermaid-js:develop' into feature/zenuml 2024-03-24 13:58:26 +11:00
Yash Singh 3613b41875 fix: subscripts in corner safari, resolves #1423 2024-03-23 18:13:05 -07:00
Sidharth VinodandGitHub 44eeceb389 Merge pull request #1420 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-03-23 05:26:20 +00:00
renovate[bot]andGitHub 5276972caa chore(deps): update all non-major dependencies 2024-03-21 02:18:50 +00:00
dontry 664d81f6ea Refactor diagram registration without top-level promise 2024-03-15 23:22:01 +11:00
Sidharth Vinodanddontry 7eee9b7a42 Bump version 2024-03-15 23:22:01 +11:00
dontry 50b0d12353 Replace zenuml example with a more sophisticated one 2024-03-15 23:22:01 +11:00
dontry 19702f885a Update dependencies and error handling in mermaid.ts 2024-03-15 23:22:01 +11:00
dontry e74828b2c8 1. Add @mermaid-js/mermaid-zenuml dependency and register external diagrams
2. Add ZenUML sample diagram to Preset.svelte component
3. Refactor diagram button layout with grid layout
2024-03-15 23:22:01 +11:00
Sidharth VinodandGitHub 1e3248cdba Merge pull request #1399 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-03-12 07:31:21 +00:00
renovate[bot]andGitHub 41e6dafd3b chore(deps): update all non-major dependencies 2024-03-11 17:37:50 +00:00
Sidharth VinodandGitHub ef1390f129 Merge pull request #1416 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-03-11 05:52:43 +00:00
renovate[bot]andGitHub e95ccb4029 chore(deps): update all non-major dependencies 2024-03-11 01:46:00 +00:00
Sidharth VinodandGitHub 301d39d50b Merge pull request #1413 from mermaid-js/sidv/10.9.0
Mermaid v10.9.0
2024-03-05 23:09:05 +05:30
Sidharth VinodandGitHub 6428d69952 Merge branch 'develop' into sidv/10.9.0 2024-03-05 23:04:09 +05:30
Sidharth VinodandGitHub 873481e5f0 Merge pull request #1398 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-03-05 23:03:40 +05:30
Sidharth Vinod c06d98c1ee Fix prettier 2024-03-05 23:03:22 +05:30
Sidharth Vinod 89d6d355b2 mermaid v10.9.0 2024-03-05 23:00:51 +05:30
renovate[bot]andGitHub 2d6e74a754 chore(deps): update all non-major dependencies 2024-03-05 17:28:42 +00:00
Sidharth VinodandGitHub ef34b4ca75 Merge pull request #1402 from mermaid-js/browserslist-update
build: update Browserslist db
2024-03-05 22:58:07 +05:30
Max Stanley 2d3f233b5d Include FA Stylesheet in SVG 2024-03-02 16:34:35 +00:00
Max Stanley ab2a024471 Formatting 2024-03-02 16:13:04 +00:00
Max Stanley 5a74e15a22 Remove whitespace 2024-03-02 16:10:35 +00:00
Max Stanley 74be138670 Add #681 TODO 2024-03-02 16:09:08 +00:00
Max Stanley fe3e283b3e Move FA CSS to app.postcss 2024-03-02 14:44:52 +00:00
Max Stanley e5e6045723 Fix indentation 2024-03-02 14:27:08 +00:00
Max Stanley d439963e4d Vendor Font-Awesome 2024-03-02 14:19:34 +00:00
GitHub Action 05a941e659 build: update Browserslist db 2024-03-01 02:18:16 +00:00
GitHub Action edc8af2157 build: update Browserslist db 2024-03-01 02:17:33 +00:00
Sidharth Vinod a14970d481 Bump node version 2024-02-26 21:32:31 +05:30
Sidharth Vinod 3e52f07364 Update node 2024-02-26 21:09:20 +05:30
Sidharth VinodandGitHub e6dd0fa83e Merge pull request #1404 from BaumiCoder/fixLinkToTutorial
Fix broken link to the tutorial page
2024-02-19 13:27:19 +05:30
Sidharth VinodandGitHub a5018276f3 Merge pull request #1405 from BaumiCoder/fixLinkToContributionGuidelines
Fix Link to contribution guidelines
2024-02-19 13:26:46 +05:30
Michael Baumgartner c005cf4bfa Fix Link to contribution guidelines
The linked markdown file in the github repository only contains
the path to the actual file with the guidelines. Furthermore,
there is a rendered version on the website, which shows the diagramms
and not the code of them.
2024-02-16 19:39:59 +01:00
Michael Baumgartner 9d2c8ea316 Fix broken link to the tutorial page 2024-02-16 19:17:13 +01:00
Sidharth Vinod b7ccfff91d Hide overflow for body 2024-02-12 14:12:57 +05:30
Sidharth Vinod ab7aed0146 feat: Add privacy modal 2024-02-09 11:16:31 +05:30
Sidharth VinodandGitHub 3f95ce271b Merge pull request #1394 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-02-06 14:49:22 +00:00
renovate[bot]andGitHub 2fb1ce7452 chore(deps): update all non-major dependencies 2024-02-06 13:21:27 +00:00
Sidharth VinodandGitHub b5e6fb255c Merge pull request #1393 from mermaid-js/mermaid/10.8.0
Add block diagram sample
2024-02-05 09:23:38 +00:00
Knut Sveidqvist 351d46c5e2 Updated block diagram example 2024-02-05 10:21:54 +01:00
Sidharth Vinod b3955bd85c Add block sample 2024-02-03 11:23:19 +05:30
Sidharth VinodandGitHub 756c8ae3ac Merge pull request #1389 from mermaid-js/browserslist-update
build: update Browserslist db
2024-02-03 11:08:51 +05:30
Sidharth VinodandGitHub 8c3e208be7 Merge pull request #1391 from mermaid-js/mermaid/10.8.0
Update mermaid v10.8.0
2024-02-03 11:08:42 +05:30
Sidharth VinodandGitHub 7714c8f8a5 Merge branch 'develop' into mermaid/10.8.0 2024-02-03 11:04:16 +05:30
Sidharth VinodandGitHub 9fb2c6ff9e Merge pull request #1388 from Abrifq/develop
Update the docs domain in the docMap test
2024-02-03 11:04:07 +05:30
Sidharth Vinod 1a935bce4f Update mermaid v10.8.0 2024-02-03 10:54:29 +05:30
GitHub Action 82d77ae2ad build: update Browserslist db 2024-02-01 02:17:52 +00:00
Abrifq b40990a528 Whoops! 2024-01-31 16:37:48 +03:00
Abrifq 66ef322a31 Updated docs domain name in the test as well 2024-01-31 16:29:05 +03:00
Sidharth VinodandGitHub 2f1c452021 Merge pull request #1384 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-01-30 15:27:33 +00:00
Sidharth VinodandGitHub d947dc9dfb Merge pull request #1335 from Abrifq/develop
Update docs links in the Editor's Docs button
2024-01-30 15:26:28 +00:00
Abrifq 57eea0f8d5 Changed docs domain to mermaid.js.org 2024-01-30 13:18:58 +03:00
Arda AydınandGitHub 193cbdde91 Merge branch 'mermaid-js:develop' into develop 2024-01-30 11:46:00 +02:00
renovate[bot]andGitHub c6de1cd453 chore(deps): update all non-major dependencies 2024-01-30 09:38:51 +00:00
Sidharth VinodandGitHub 0c6a43eac7 Merge pull request #1385 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-01-30 09:38:01 +00:00
renovate[bot]andGitHub 45687fa76b chore(deps): update all non-major dependencies 2024-01-29 19:50:46 +00:00
Arda AydınandGitHub 6b6adf033a Merge branch 'develop' into develop 2024-01-28 18:06:29 +02:00
Sidharth VinodandGitHub 65d6931336 Merge pull request #1355 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-01-23 02:33:25 +00:00
renovate[bot]andGitHub 38a15f0a24 chore(deps): update all non-major dependencies 2024-01-23 01:46:19 +00:00
Sidharth VinodandGitHub 31187f8b73 Merge pull request #1377 from mermaid-js/intelligentAutoSync
feat: Adaptive auto sync
2024-01-22 14:41:04 +00:00
Sidharth VinodandGitHub 6f33a03e18 Merge branch 'develop' into intelligentAutoSync 2024-01-22 19:12:15 +05:30
Sidharth VinodandGitHub d3ecdfa9ef Merge pull request #1380 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v5.0.12 [security]
2024-01-22 13:41:15 +00:00
Sidharth VinodandGitHub 0181069a08 Merge pull request #1381 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency @sveltejs/kit to v2.4.1
2024-01-22 13:41:06 +00:00
renovate[bot]andGitHub a422ca03cb chore(deps): update dependency @sveltejs/kit to v2.4.1 2024-01-22 01:15:12 +00:00
renovate[bot]andGitHub 42779d7cec chore(deps): update dependency vite to v5.0.12 [security] 2024-01-19 22:39:12 +00:00
Sidharth Vinod 61928528a7 remove fontawesome 2024-01-18 23:20:33 +05:30
Sidharth Vinod 2acb84415a feat: waitForRender 2024-01-18 23:20:21 +05:30
Sidharth Vinod 2033f1add0 Wait to sync before export 2024-01-18 21:24:42 +05:30
Sidharth Vinod 675af38c49 Throw error if out of sync 2024-01-18 21:05:33 +05:30
Sidharth Vinod 0c7d48970c Strict match filename 2024-01-18 20:48:32 +05:30
Sidharth Vinod 71cf898f5c Wait for diagram to sync 2024-01-18 20:32:05 +05:30
Sidharth Vinod 3e457fb0bb Revert "Bump cypress"
This reverts commit d9eba9ceec.
2024-01-18 20:26:26 +05:30
Sidharth VinodandGitHub 2eb9cff6bb Merge pull request #1364 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-01-18 14:52:51 +00:00
Sidharth Vinod 45d41b55eb Remove use of cy.clock 2024-01-18 20:16:40 +05:30
Sidharth Vinod 9bf4214617 Merge branch 'develop' into intelligentAutoSync
* develop:
  Mermaid 10.7.0
  Add UTM tags
  build: update Browserslist db
2024-01-18 19:20:49 +05:30
renovate[bot]andGitHub 59019417de chore(deps): update all non-major dependencies 2024-01-18 13:49:18 +00:00
Sidharth Vinod 91e6a3f1ba Mermaid 10.7.0 2024-01-18 19:18:23 +05:30
Sidharth Vinod d9eba9ceec Bump cypress 2024-01-18 19:17:05 +05:30
Sidharth Vinod e6e2b62e82 test: Verify adaptive auto sync 2024-01-17 22:28:43 +05:30
Sidharth VinodandGitHub c2beff5442 Merge pull request #1378 from mermaid-js/janPromo
Add UTM tags
2024-01-17 21:54:22 +05:30
Sidharth Vinod c08df23ca7 Merge branch 'develop' into janPromo
* develop:
  Fix XSS vulnerability
  chore: Update eslint plugin and fix errors
  build: update Browserslist db
  fix: Unit tests
  chore: Fix issues
  Add node-version
  chore: Auto format and fix warnings
  chore: Upgrade dependencies
  build: update Browserslist db
  build: update Browserslist db
2024-01-17 21:50:59 +05:30
Sidharth Vinod b9b2f6ba85 Add UTM tags 2024-01-17 21:49:10 +05:30
Sidharth Vinod f0951d575f feat: Adaptive auto sync 2024-01-17 18:53:57 +05:30
Sidharth VinodandGitHub 27b158e624 Merge pull request #1371 from mermaid-js/browserslist-update
build: update Browserslist db
2024-01-15 07:01:22 +00:00
Sidharth VinodandGitHub 2a3b12ca82 Merge pull request #1372 from mermaid-js/updateDeps
Update ESLint plugin
2024-01-15 12:30:42 +05:30
Sidharth Vinodandchbi afbc7f3de3 Fix XSS vulnerability
Co-authored-by: chbi <chbi@chbi.eu>
2024-01-15 12:15:57 +05:30
Sidharth Vinod 8667c6de33 chore: Update eslint plugin and fix errors 2024-01-15 12:13:13 +05:30
GitHub Action 67d934c12d build: update Browserslist db 2024-01-15 02:25:58 +00:00
Sidharth VinodandGitHub eaecb488ec Merge pull request #1368 from mermaid-js/updateDeps
UpdateDeps
2024-01-11 17:12:52 +00:00
Sidharth Vinod beb5a8935f fix: Unit tests 2024-01-11 22:39:50 +05:30
Sidharth Vinod 6a422ca643 chore: Fix issues 2024-01-11 22:23:18 +05:30
Sidharth Vinod b487ef67b3 Add node-version 2024-01-11 22:17:17 +05:30
Sidharth Vinod 44e0026e0d Merge branch 'develop' into updateDeps
* develop:
  feat: Jan 2024 Promo
  Update promo.ts
  build: update Browserslist db
  build: update Browserslist db
2024-01-11 22:14:17 +05:30
Sidharth Vinod 3da392a4a7 Merge branch 'develop' of github.com:mermaid-js/mermaid-live-editor into develop
* 'develop' of github.com:mermaid-js/mermaid-live-editor:
  feat: Jan 2024 Promo
  build: update Browserslist db
  build: update Browserslist db
2024-01-11 22:12:29 +05:30
Sidharth Vinod b69cca3763 Merge branch 'develop' into janPromo
* develop:
  Update promo.ts
2024-01-11 22:11:04 +05:30
Sidharth VinodandGitHub 539efbae7d Merge pull request #1363 from mermaid-js/browserslist-update
build: update Browserslist db
2024-01-11 16:39:41 +00:00
Sidharth VinodandGitHub 488f655e2f Merge pull request #1367 from mermaid-js/janPromo
feat: Jan 2024 Promo
2024-01-11 22:09:23 +05:30
Sidharth Vinod 087fb897a0 chore: Auto format and fix warnings 2024-01-11 22:06:29 +05:30
Sidharth Vinod d1cf590f20 chore: Upgrade dependencies 2024-01-11 22:03:44 +05:30
Sidharth Vinod 65587a6c7d feat: Jan 2024 Promo 2024-01-11 21:32:46 +05:30
Sidharth VinodandGitHub 027f7ae76f Update promo.ts 2024-01-03 12:30:51 +05:30
GitHub Action c278f200b9 build: update Browserslist db 2024-01-01 02:26:31 +00:00
GitHub Action a4638c7d1e build: update Browserslist db 2023-12-15 02:22:38 +00:00
Sidharth VinodandGitHub 75ab8c2c58 Merge pull request #1360 from mermaid-js/release-promotion
Release live editor
2023-12-14 10:50:53 +05:30
Sidharth VinodandGitHub 1412bed156 Merge pull request #1362 from mermaid-js/sidv/fixPlausible
Fix plausible tracking
2023-12-14 10:50:23 +05:30
Sidharth Vinod 4891fc8a84 Merge branch 'develop' into sidv/fixPlausible
* develop:
2023-12-14 10:44:02 +05:30
Sidharth Vinod 1a82978f2c chore: Use manual page tracking to avoid sending hash. 2023-12-14 10:37:19 +05:30
Sidharth VinodandGitHub f771662360 Merge pull request #1361 from mermaid-js/sidv/disableHostedAnalytics
feat: Add env vars to configure plausible
2023-12-12 07:48:28 +00:00
Sidharth Vinod 434a50f81c format netlify.toml 2023-12-11 18:00:20 +05:30
Sidharth Vinod e7293cc0a9 feat: Add env vars to configure plausible 2023-12-11 17:42:38 +05:30
Sidharth VinodandGitHub af4242a46a Merge pull request #1356 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-12-11 04:28:14 +00:00
renovate[bot]andGitHub d022a22211 chore(deps): update all non-major dependencies 2023-12-11 03:52:50 +00:00
Arda AydınandGitHub db3a314589 Merge branch 'develop' into develop 2023-12-07 08:01:55 +03:00
Sidharth VinodandGitHub be2519f89c Merge pull request #1344 from mermaid-js/release-promotion
Release live editor
2023-12-06 11:32:01 +05:30
Sidharth VinodandGitHub 007b461d5e Merge pull request #1347 from maxstanley/docker_env
Provide environment variables to Docker build
2023-12-06 11:29:27 +05:30
Sidharth VinodandGitHub 0d4ae40f12 Merge pull request #1354 from mermaid-js/dependabot/npm_and_yarn/adobe/css-tools-4.3.2
build(deps): bump @adobe/css-tools from 4.3.1 to 4.3.2
2023-12-06 11:29:09 +05:30
Sidharth VinodandGitHub 5c747a1c03 Merge pull request #1357 from mermaid-js/dependabot/npm_and_yarn/vite-4.5.1
build(deps-dev): bump vite from 4.5.0 to 4.5.1
2023-12-06 11:28:54 +05:30
Sidharth Vinod f2fde65536 feat: trackBannerClick
Purposefully not enabling the outbound link tracking,
as it would track links people have embedded in diagrams
which will then be available in public analytics.
2023-12-06 11:27:16 +05:30
Sidharth Vinod 7ff3594df7 Merge branch 'master' into develop
* master:
  chore: Update MC promo link
  remove toSorted
  chore: Tweak start and end dates
  feat: Add Holiday2023 banner
2023-12-06 11:25:16 +05:30
dependabot[bot]andGitHub 47537f6594 build(deps-dev): bump vite from 4.5.0 to 4.5.1
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.1/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-06 00:04:15 +00:00
Sidharth VinodandGitHub f52699400e Merge pull request #1353 from mermaid-js/browserslist-update
build: update Browserslist db
2023-12-05 04:49:08 +00:00
Sidharth VinodandGitHub 5a165f0773 Apply suggestions from code review 2023-12-05 10:18:26 +05:30
Sidharth Vinod 19a15e9710 chore: Update MC promo link 2023-12-01 12:00:02 +05:30
dependabot[bot]andGitHub 697201f1e0 build(deps): bump @adobe/css-tools from 4.3.1 to 4.3.2
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-01 05:24:23 +00:00
GitHub Action 4e93f44121 build: update Browserslist db 2023-12-01 02:26:53 +00:00
Max Stanley 5865b46a26 Simplify docker action 2023-11-30 18:06:19 +00:00
Sidharth VinodandGitHub 9507c67f73 Merge pull request #1348 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update dependency svelte-preprocess to v5.1.1
2023-11-30 04:41:49 +00:00
renovate[bot]andGitHub 4ccf0dc643 chore(deps): update dependency svelte-preprocess to v5.1.1 2023-11-29 16:28:09 +00:00
Sidharth VinodandGitHub 9bfec1f13d Merge pull request #1349 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-11-29 16:27:26 +00:00
Sidharth VinodandGitHub dff33bc869 Merge pull request #1351 from mermaid-js/sidv/holiday2023
feat: Add Holiday2023 banner
2023-11-29 11:02:27 +05:30
Sidharth Vinod 670b298ba6 remove toSorted 2023-11-29 10:08:15 +05:30
renovate[bot]andGitHub b25a9d3e97 chore(deps): update all non-major dependencies 2023-11-28 21:12:26 +00:00
Sidharth Vinod 1f28dde6e7 chore: Tweak start and end dates 2023-11-28 14:27:17 +05:30
Sidharth Vinod 9ba2598b00 feat: Add Holiday2023 banner 2023-11-28 14:25:14 +05:30
Max Stanley d2d082ec08 Always push tag event 2023-11-21 22:08:25 +00:00
Max Stanley f6a729f871 Remove env 2023-11-21 22:06:16 +00:00
Max Stanley 6d983158b6 Fix env 2023-11-21 22:03:11 +00:00
Max Stanley 2d7f91ab26 Add new-line 2023-11-21 22:01:13 +00:00
Max Stanley 7db761fba7 Improved docker CI 2023-11-21 21:59:03 +00:00
Max Stanley 11a814ac7d Add environment arguments to Dockerfile 2023-11-21 21:00:14 +00:00
Sidharth VinodandGitHub 4ad6bf915b Merge pull request #1342 from mermaid-js/browserslist-update
build: update Browserslist db
2023-11-20 11:01:27 +00:00
Sidharth VinodandGitHub b156fe9966 Merge pull request #1345 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-11-20 11:01:04 +00:00
renovate[bot]andGitHub 2611664047 chore(deps): update all non-major dependencies 2023-11-20 01:29:47 +00:00
Sidharth VinodandGitHub d77f5c8399 Merge pull request #1339 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-11-19 18:19:55 +00:00
renovate[bot]andGitHub f5f9ec9560 chore(deps): update all non-major dependencies 2023-11-19 12:04:12 +00:00
Sidharth VinodandGitHub 454c6d836c Merge pull request #1340 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-11-19 12:03:31 +00:00
renovate[bot]andGitHub 1b004a76b2 chore(deps): update all non-major dependencies 2023-11-17 23:09:27 +00:00
GitHub Action 517178a7e7 build: update Browserslist db 2023-11-15 02:20:31 +00:00
Arda AydınandGitHub d84947e33d Merge branch 'develop' into develop 2023-11-10 01:56:33 +03:00
Sidharth VinodandGitHub 3f64df7754 Merge pull request #1336 from mermaid-js/release-promotion
Release live editor
2023-11-09 23:33:14 +05:30
Sidharth VinodandGitHub fd411e46be Merge pull request #1302 from HK-SHAO/develop
fix: interface SVG was changed by mistake
2023-11-09 23:32:34 +05:30
Sidharth VinodandGitHub 796d1b98c6 Merge pull request #1321 from subhash-halder/add-xychart-to-mermaid-live-editor
Added XYChart sample in mermaid live ediror
2023-11-09 23:31:46 +05:30
Sidharth VinodandGitHub 8cb8b14093 Merge pull request #1309 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-11-09 08:01:11 +00:00
Sidharth VinodandGitHub fad000984a Merge branch 'develop' into develop 2023-11-09 13:24:45 +05:30
renovate[bot]andGitHub 7c9ee990b5 chore(deps): update all non-major dependencies 2023-11-09 07:53:59 +00:00
Sidharth VinodandGitHub bbcd207cb7 Merge pull request #1317 from mozi47/fix/hambergur-menu-toggling
Hamberger Menu Fixed
2023-11-09 07:53:15 +00:00
Sidharth VinodandGitHub 93215b3391 Merge pull request #1308 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-11-09 07:50:16 +00:00
Sidharth VinodandGitHub 97dad2419b Merge pull request #1333 from mermaid-js/release-promotion
Release live editor
2023-11-09 11:51:59 +05:30
renovate[bot]andGitHub abb494d768 chore(deps): update all non-major dependencies 2023-11-09 06:21:06 +00:00
Sidharth VinodandGitHub 3c08315a85 Merge pull request #1310 from mermaid-js/dependabot/npm_and_yarn/get-func-name-2.0.2
build(deps): bump get-func-name from 2.0.0 to 2.0.2
2023-11-09 06:20:21 +00:00
Sidharth VinodandGitHub 863efb5746 Merge pull request #1327 from shubhusion/develop
Absolute path for /mermaidchart-logo.svg #1319
2023-11-09 06:18:21 +00:00
Sidharth VinodandGitHub 1250cc5456 Merge pull request #1316 from mermaid-js/renovate/npm-postcss-vulnerability
chore(deps): update dependency postcss to v8.4.31 [security]
2023-11-09 06:15:45 +00:00
Sidharth VinodandGitHub ecc6d2a539 Merge pull request #1318 from mermaid-js/browserslist-update
build: update Browserslist db
2023-11-09 06:15:21 +00:00
renovate[bot]andGitHub 4af85ae30b chore(deps): update dependency postcss to v8.4.31 [security] 2023-11-09 06:13:54 +00:00
Sidharth VinodandGitHub baff40a692 Merge pull request #1328 from mermaid-js/renovate/mermaid-packages
fix(deps): update dependency mermaid to v10.6.1
2023-11-09 11:42:28 +05:30
Sidharth VinodandGitHub 00b084721a Merge pull request #1330 from crpz1/bug/1181_export-png-with-theme-background
Use current theme's background color on PNG export
2023-11-09 06:11:50 +00:00
Abrifq 74c0e6dcad Update tests for the new doc links 2023-11-08 14:22:18 +03:00
renovate[bot]andGitHub 07b0a065fa fix(deps): update dependency mermaid to v10.6.1 2023-11-06 16:04:10 +00:00
Abrifq fd8bba4934 Fixed sankey's docKey
Whoops!
2023-11-05 22:04:37 +03:00
Arda AydınandGitHub 4f5b0cde29 Merge branch 'mermaid-js:develop' into develop 2023-11-05 21:46:47 +03:00
Abrifq e15e5378f7 Added new Mermaid types to Editor Doc Links 2023-11-05 21:45:38 +03:00
Arda AydınandGitHub 4eefa7083e Update Doc links on Editor 2023-11-03 04:25:25 +03:00
Sidharth VinodandGitHub ba12c7ab2f Merge pull request #1332 from zhzy0077/patch-1
DOCUMENTATION button goes to 404
2023-11-02 06:12:53 +00:00
Zhiyuan ZhengandGitHub df49ead019 DOCUMENTATION button goes to 404
This should be https://mermaid.js.org/intro/getting-started.html
2023-11-01 16:08:17 +08:00
GitHub Action 3ef4db3ecd build: update Browserslist db 2023-11-01 02:19:13 +00:00
crpz1 5d9d3feb59 Use current theme's background color on PNG export 2023-10-31 15:56:55 +00:00
shubhusion 5a155d285b Absolute path for /mermaidchart-logo.svg #1319 2023-10-27 22:35:43 +05:30
Subhash Halder d56d2678d4 Added XYChart sample in mermaid live ediror 2023-10-18 19:48:44 +05:30
GitHub Action 0a600d1c5a build: update Browserslist db 2023-10-15 02:17:46 +00:00
Muzakir ShahandGitHub f7f77f35d3 Hamberger Menu Fixed
Hamberger menu is fixed and the menu UI is also updated.
2023-10-06 18:26:52 +05:00
Sidharth VinodandGitHub 02adac1adf Merge pull request #1307 from mermaid-js/release-promotion
Release live editor
2023-10-06 11:04:33 +05:30
Sidharth Vinod 414c5bb59b Update mermaid 2023-10-06 11:02:43 +05:30
Sidharth VinodandGitHub 9ef8105ab1 Merge pull request #1311 from mermaid-js/browserslist-update
build: update Browserslist db
2023-10-06 10:55:03 +05:30
Sidharth VinodandGitHub 18ed1c9695 Merge pull request #1303 from Yokozuna59/feat/support-sankey-highlighting
feat: support `sankey` diagram highlighting
2023-10-06 10:54:42 +05:30
GitHub Action 173c7260c1 build: update Browserslist db 2023-10-01 02:20:39 +00:00
dependabot[bot]andGitHub 77ba621d90 build(deps): bump get-func-name from 2.0.0 to 2.0.2
Bumps [get-func-name](https://github.com/chaijs/get-func-name) from 2.0.0 to 2.0.2.
- [Release notes](https://github.com/chaijs/get-func-name/releases)
- [Commits](https://github.com/chaijs/get-func-name/commits/v2.0.2)

---
updated-dependencies:
- dependency-name: get-func-name
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-27 21:32:44 +00:00
Sidharth VinodandGitHub 83382901cd Merge pull request #1304 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-09-21 14:42:35 +00:00
renovate[bot]andGitHub 580e6657e1 chore(deps): update all non-major dependencies 2023-09-20 22:29:28 +00:00
Reda Al Sulais d0dcbf37e1 feat: support sankey diagram highlighting 2023-09-16 15:02:06 +03:00
HK-SHAOandGitHub bb09d2ed51 Merge pull request #1 from HK-SHAO/HK-SHAO-patch-interface-changed
fix: interface SVG was changed by mistake
2023-09-15 17:08:36 +08:00
HK-SHAOandGitHub f0ce4c9db6 fix: interface SVG was changed by mistake
When setting up an export of different sizes, click the Copy Image button or the Download button, and the SVG size of the user interface changes
2023-09-15 17:07:19 +08:00
Sidharth VinodandGitHub 675ecccbf6 Merge pull request #1301 from mermaid-js/release-promotion
Release live editor
2023-09-15 13:32:54 +05:30
Sidharth Vinod e504f25680 Mermaid 10.4.0 2023-09-15 13:31:44 +05:30
Sidharth Vinod e530b3e35f Merge branch 'develop' into sidv/fixErrorDiagram
* develop:
  build: update Browserslist db
  Fix tests
  fix cypress
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  Group mermaid release separately
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  build: update Browserslist db
  Bump @adobe/css-tools from 4.3.0 to 4.3.1
  fix: add missing and remove duplicate keywords in pie
2023-09-15 10:36:37 +05:30
Sidharth VinodandGitHub 2cf66be68f Merge pull request #1297 from mermaid-js/release-promotion
Release live editor
2023-09-15 10:17:02 +05:30
Sidharth VinodandGitHub 75ad46bdb6 Merge pull request #1298 from mermaid-js/browserslist-update
build: update Browserslist db
2023-09-15 09:02:43 +05:30
GitHub Action bde9b1b7c7 build: update Browserslist db 2023-09-15 02:15:23 +00:00
Sidharth VinodandGitHub d4ebb23232 Merge pull request #1280 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-09-14 20:29:22 +00:00
Sidharth Vinod b0b13c2d1e Fix tests 2023-09-15 01:53:46 +05:30
Sidharth VinodandGitHub 3a6abf5128 Merge pull request #1278 from mermaid-js/release-promotion
Release live editor
2023-09-15 01:40:08 +05:30
Sidharth Vinod 67ceb7ad77 fix cypress 2023-09-15 01:35:54 +05:30
Sidharth VinodandGitHub 8bba1ade92 Merge branch 'master' into release-promotion 2023-09-15 01:26:36 +05:30
renovate[bot]andGitHub 6e09fa34fe chore(deps): update all non-major dependencies 2023-09-14 19:56:17 +00:00
Sidharth VinodandGitHub cf58bb1bb5 Merge pull request #1296 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-09-14 19:55:28 +00:00
renovate[bot]andGitHub aa660c838b chore(deps): update all non-major dependencies 2023-09-14 16:48:03 +00:00
Sidharth Vinod d34706f1c8 Group mermaid release separately 2023-09-14 13:26:19 +05:30
Sidharth VinodandGitHub 39eba2673f Merge pull request #1283 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-09-06 06:58:29 +00:00
Sidharth VinodandGitHub ca4afee296 Merge pull request #1293 from mermaid-js/dependabot/npm_and_yarn/adobe/css-tools-4.3.1
Bump @adobe/css-tools from 4.3.0 to 4.3.1
2023-09-06 06:58:16 +00:00
renovate[bot]andGitHub 60273f59a1 chore(deps): update all non-major dependencies 2023-09-05 15:13:03 +00:00
Sidharth VinodandGitHub 62d81d33b2 Merge pull request #1285 from mermaid-js/browserslist-update
build: update Browserslist db
2023-09-05 13:12:41 +00:00
Sidharth VinodandGitHub 7261334c85 Merge pull request #1292 from Yokozuna59/fix/add-missing-keywords-to-pie
fix: add missing and remove duplicate keywords in `pie`
2023-09-05 13:12:25 +00:00
GitHub Action 6b46fe4a2b build: update Browserslist db 2023-09-01 02:17:51 +00:00
GitHub Action 1698f2f5c0 build: update Browserslist db 2023-09-01 02:17:07 +00:00
dependabot[bot]andGitHub 8362b3e265 Bump @adobe/css-tools from 4.3.0 to 4.3.1
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.0 to 4.3.1.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-30 09:02:04 +00:00
Reda Al Sulais 557e54e82b fix: add missing and remove duplicate keywords in pie 2023-08-28 13:57:37 +03:00
Sidharth Vinod 6334d4ab84 fix: Error diagram rendering 2023-08-17 14:26:04 +05:30
Sidharth VinodandGitHub 1c8201a204 Add --platform linux/amd64 to run command 2023-08-17 04:31:15 +00:00
Sidharth VinodandGitHub 3e2d35a5ae Merge pull request #1279 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-08-12 14:15:58 +00:00
renovate[bot]andGitHub c73f22c947 chore(deps): update all non-major dependencies 2023-08-11 17:28:03 +00:00
Sidharth VinodandGitHub f23b8b00fa Merge pull request #1274 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-08-02 18:49:31 +00:00
renovate[bot]andGitHub 98d9045555 chore(deps): update all non-major dependencies 2023-08-02 18:48:10 +00:00
Sidharth VinodandGitHub ca874162fc Merge pull request #1275 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-08-02 18:47:10 +00:00
Sidharth VinodandGitHub 18d20550fd Merge pull request #1277 from mermaid-js/browserslist-update
build: update Browserslist db
2023-08-02 18:47:02 +00:00
renovate[bot]andGitHub 0bc4e098b5 chore(deps): update all non-major dependencies 2023-08-01 16:03:11 +00:00
GitHub Action f607ab4564 build: update Browserslist db 2023-08-01 02:32:24 +00:00
Sidharth VinodandGitHub 8194a3ff86 Merge pull request #1272 from mermaid-js/release-promotion
Release live editor
2023-07-28 16:59:23 +05:30
Sidharth VinodandGitHub 281af4a242 Merge pull request #1264 from mermaid-js/dependabot/npm_and_yarn/semver-5.7.2
Bump semver from 5.7.1 to 5.7.2
2023-07-27 09:29:46 +00:00
Sidharth VinodandGitHub ad86f4b678 Merge pull request #1263 from mermaid-js/release-promotion
Release live editor
2023-07-27 14:57:28 +05:30
dependabot[bot]andGitHub 9e5323c9a1 Bump semver from 5.7.1 to 5.7.2
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-27 09:22:17 +00:00
Sidharth VinodandGitHub 6829b98854 Merge pull request #1267 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-07-27 09:20:25 +00:00
Sidharth Vinod 62a5012efd Fix node version 2023-07-27 14:46:13 +05:30
renovate[bot]andGitHub 49bf28b1a2 chore(deps): update all non-major dependencies 2023-07-26 10:02:11 +00:00
Sidharth VinodandGitHub 047ba5228b Merge pull request #1269 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-07-25 13:16:44 +00:00
renovate[bot]andGitHub 4bd3c35bc0 chore(deps): update all non-major dependencies 2023-07-24 22:22:29 +00:00
Sidharth VinodandGitHub b789053031 Merge pull request #1268 from mermaid-js/dependabot/npm_and_yarn/word-wrap-1.2.4
Bump word-wrap from 1.2.3 to 1.2.4
2023-07-19 07:05:02 +00:00
dependabot[bot]andGitHub 9478ef0184 Bump word-wrap from 1.2.3 to 1.2.4
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-19 02:45:04 +00:00
Sidharth VinodandGitHub e3b7a31a6b Merge pull request #1265 from mermaid-js/browserslist-update
build: update Browserslist db
2023-07-18 16:41:09 +00:00
Sidharth VinodandGitHub 157c82ba81 Merge pull request #1266 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-07-17 09:05:40 +00:00
renovate[bot]andGitHub 3510ed3e63 chore(deps): update all non-major dependencies 2023-07-17 00:24:27 +00:00
GitHub Action e8d970102b build: update Browserslist db 2023-07-15 02:56:04 +00:00
Sidharth VinodandGitHub 0e5030e95d Merge pull request #1262 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-07-10 18:21:55 +00:00
renovate[bot]andGitHub 88be72cee5 chore(deps): update all non-major dependencies 2023-07-10 01:46:31 +00:00
Sidharth VinodandGitHub 0e5c4c4f5d Merge pull request #1257 from mermaid-js/release-promotion
Release live editor
2023-07-07 16:15:59 +05:30
Sidharth VinodandGitHub 1a7cbab88f Merge pull request #1256 from nirname/fix-docker-ports
Fixed docker ports for local development
2023-07-07 07:00:42 +00:00
Sidharth VinodandGitHub 30817c53f1 Merge pull request #1259 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-07-07 06:59:54 +00:00
renovate[bot]andGitHub 90f1dbf176 chore(deps): update all non-major dependencies 2023-07-06 12:03:30 +00:00
Sidharth VinodandGitHub d24a9e7fda Merge pull request #1255 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-07-02 06:14:12 +00:00
Sidharth VinodandGitHub 257dab1407 Merge pull request #1258 from mermaid-js/browserslist-update
build: update Browserslist db
2023-07-02 06:13:49 +00:00
renovate[bot]andGitHub 5ebb0d2883 chore(deps): update all non-major dependencies 2023-07-01 03:53:47 +00:00
GitHub Action b8015c3a23 build: update Browserslist db 2023-07-01 03:01:01 +00:00
Sidharth VinodandGitHub 336422bd48 Merge pull request #1254 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-06-28 15:12:17 +05:30
Sidharth VinodandGitHub bc03fa4ef8 Merge pull request #1253 from mermaid-js/browserslist-update
build: update Browserslist db
2023-06-28 15:12:05 +05:30
renovate[bot]andGitHub abcbd6a698 chore(deps): update all non-major dependencies 2023-06-28 07:49:02 +00:00
Nikolay Rozhkov ea0669a62b Fixed docker ports for local development 2023-06-20 03:57:09 +03:00
GitHub Action d2adcd1429 build: update Browserslist db 2023-06-15 02:35:39 +00:00
Sidharth Vinod a3c4c79788 Revert "Outbound link tracking"
This reverts commit 094f451a82.
2023-06-14 23:02:26 +05:30
Sidharth VinodandGitHub ac569f8eca Merge pull request #1252 from mermaid-js/release-promotion
Release live editor
2023-06-14 22:56:33 +05:30
Sidharth VinodandGitHub 6a1d24f2e7 Merge pull request #1251 from mermaid-js/sidv/save
Outbound link tracking
2023-06-14 22:56:02 +05:30
Sidharth Vinod f63c416a3d Update links 2023-06-14 22:55:23 +05:30
Sidharth Vinod 094f451a82 Outbound link tracking 2023-06-14 22:50:08 +05:30
Knut SveidqvistandGitHub c68ffa8cb1 Merge pull request #1250 from mermaid-js/release-promotion
Release live editor
2023-06-14 18:20:43 +02:00
Knut SveidqvistandGitHub 8e15941c35 Merge branch 'master' into release-promotion 2023-06-14 18:12:59 +02:00
Knut SveidqvistandGitHub 3444b895b1 Merge pull request #1249 from mermaid-js/sidv/save
Add save to mermaidchart button
2023-06-14 15:59:34 +00:00
Sidharth Vinod a95d02427f Add save to mermaidchart button 2023-06-14 16:25:57 +05:30
Sidharth VinodandGitHub 59f76003b3 Merge pull request #1248 from mermaid-js/release-promotion
Release live editor
2023-06-13 10:16:59 +05:30
Sidharth Vinod 68e2aa4437 Fix error alignment 2023-06-13 10:05:07 +05:30
Sidharth VinodandGitHub 471c0504c6 Merge pull request #1247 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-06-12 11:27:39 +05:30
Sidharth VinodandGitHub 6930f3e4cc Merge pull request #1246 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-06-12 11:27:19 +05:30
renovate[bot]andGitHub 9344ad88dd chore(deps): update all non-major dependencies 2023-06-12 01:14:09 +00:00
renovate[bot]andGitHub 0ce87fab09 chore(deps): update all non-major dependencies 2023-06-12 01:13:57 +00:00
Sidharth VinodandGitHub aa2a2a2dd1 Merge pull request #1245 from mermaid-js/release-promotion
Release live editor
2023-06-10 23:51:21 +05:30
Sidharth VinodandGitHub 925a0acd82 Merge branch 'master' into release-promotion 2023-06-10 23:46:02 +05:30
Sidharth Vinod 2e784599b1 chore: mermaid@10.2.3 2023-06-10 23:45:19 +05:30
Sidharth VinodandGitHub a5383887ee Merge pull request #1241 from mermaid-js/release-promotion
Release live editor
2023-06-10 02:38:28 +05:30
Sidharth Vinod 6f123d6026 Add analytics comment. 2023-06-10 01:51:11 +05:30
Sidharth Vinod 78fec6411c Merge branch 'master' into develop
* master:
  Add lengthBucket
  Add QuardantChart and Mindmap to diagram types
  Update plausible
  Update license
2023-06-10 01:50:27 +05:30
Sidharth Vinod dc735e2d08 Add lengthBucket 2023-06-09 23:29:06 +05:30
Sidharth Vinod ca5610ab79 Add QuardantChart and Mindmap to diagram types 2023-06-09 23:18:52 +05:30
Sidharth Vinod 7db475acb2 Update plausible 2023-06-09 18:42:30 +05:30
Sidharth VinodandGitHub 8c9bf1ace3 Merge pull request #1242 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-06-07 17:54:55 +00:00
renovate[bot]andGitHub fb54fabd1e chore(deps): update all non-major dependencies 2023-06-06 19:45:45 +00:00
Sidharth VinodandGitHub 0c690fcd0e Merge pull request #1232 from ztec/bug/1230updateDockerPort
Fix #1230 Docker: use 8080 port instead of 80 to reduce privilege requirements
2023-06-04 11:56:56 +05:30
Sidharth VinodandGitHub bb98ba3456 Merge pull request #1240 from mermaid-js/release-promotion
Release live editor
2023-06-03 15:59:18 +05:30
Sidharth VinodandGitHub fce17b96cc Merge branch 'master' into release-promotion 2023-06-03 15:52:38 +05:30
Sidharth VinodandGitHub d64f6c1a1b Merge pull request #1228 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-06-03 10:21:43 +00:00
renovate[bot]andGitHub 51fe584fb3 chore(deps): update all non-major dependencies 2023-06-03 02:10:07 +00:00
Sidharth VinodandGitHub 01b79503c9 Merge pull request #1234 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-06-03 00:21:24 +05:30
Sidharth VinodandGitHub 38ca72ce84 Merge pull request #1233 from mermaid-js/release-promotion
Release live editor
2023-06-02 18:38:14 +05:30
renovate[bot]andGitHub c1542619e9 chore(deps): update all non-major dependencies 2023-06-02 13:07:45 +00:00
Sidharth VinodandGitHub 082c9f725b Merge branch 'master' into release-promotion 2023-06-02 18:37:39 +05:30
Sidharth VinodandGitHub fdbd25ca65 Merge pull request #1231 from mermaid-js/release/10.2.0
Release 10.2.0
2023-06-02 18:37:04 +05:30
Sidharth Vinod a355922f25 Merge branch 'develop' into release/10.2.0
* develop:
  build: update Browserslist db
2023-06-02 18:23:07 +05:30
Sidharth VinodandGitHub 5d6e2c71b0 Merge pull request #1239 from mermaid-js/browserslist-update
build: update Browserslist db
2023-06-02 18:22:50 +05:30
Sidharth Vinod cbafaf6cc4 Upgrade mermaid 10.2.2 2023-06-02 18:22:22 +05:30
Sidharth Vinod 28272e985d Merge branch 'develop' into release/10.2.0
* develop:
  Added quadrantChart in the live editor with sample
  chore(deps): update all non-major dependencies
2023-06-02 12:38:54 +05:30
Sidharth Vinod 87ad2d68c6 Upgrade mermaid 10.2.1 2023-06-02 12:36:46 +05:30
GitHub Action 8b1a2899cc build: update Browserslist db 2023-06-01 03:06:04 +00:00
Sidharth VinodandGitHub b1538f6bad Merge pull request #1237 from amsubhash/add-quadrantchart-to-mermaid-live-editor
Added quadrantChart in the live editor with sample
2023-05-31 08:58:45 +05:30
Subhash Halder b2b284c118 Added quadrantChart in the live editor with sample 2023-05-30 21:42:17 +05:30
Sidharth VinodandGitHub 8e2f8b6804 Merge pull request #1227 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-05-26 12:17:07 +00:00
ztec c3b711c5cd Fix #1230 Docker: use 8080 port instead of 80 to reduce privilege requirements 2023-05-26 11:01:45 +02:00
renovate[bot]andGitHub e1926f0e83 chore(deps): update all non-major dependencies 2023-05-26 08:48:04 +00:00
Sidharth Vinod d2d2f4bc43 Release 10.2.0 2023-05-26 12:15:55 +05:30
Sidharth VinodandGitHub 9a46b199cf Merge pull request #1218 from mermaid-js/release-promotion
Release live editor
2023-05-17 00:43:30 +05:30
Sidharth Vinod 16ac30da7c Update license 2023-05-16 23:52:33 +05:30
Sidharth VinodandGitHub b06fd02dcb Merge pull request #1222 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-05-16 18:15:51 +00:00
renovate[bot]andGitHub 84c7f4e837 chore(deps): update all non-major dependencies 2023-05-16 17:37:06 +00:00
Sidharth VinodandGitHub bf0700853e Merge pull request #1221 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-05-16 17:36:22 +00:00
Sidharth VinodandGitHub bf90c3c0e3 Merge pull request #1224 from mermaid-js/browserslist-update
build: update Browserslist db
2023-05-16 17:36:09 +00:00
renovate[bot]andGitHub d19fbd1b5b chore(deps): update all non-major dependencies 2023-05-16 07:49:38 +00:00
GitHub Action d9bc26cd59 build: update Browserslist db 2023-05-15 02:29:41 +00:00
Sidharth VinodandGitHub b1bcc5a20f Merge pull request #1220 from enchant97-forks/develop
Use nginx-unprivileged
2023-05-09 06:18:36 +00:00
Leo Spratt a8c0fb111c Use nginx-unprivileged
This allows the Docker image to run as an unprivileged user, due to
changes in Docker; the port remains as 80.
2023-05-07 18:03:19 +01:00
Sidharth VinodandGitHub 635345ea66 Merge pull request #1213 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-05-02 07:59:22 +00:00
renovate[bot]andGitHub 5c4cc9a3f9 chore(deps): update all non-major dependencies 2023-05-02 05:32:20 +00:00
Sidharth VinodandGitHub dd58ac8146 Merge pull request #1217 from mermaid-js/renovate/node-20.x
chore(deps): update node.js to v20
2023-05-02 05:30:59 +00:00
renovate[bot]andGitHub f3beacfae1 chore(deps): update node.js to v20 2023-05-01 06:32:50 +00:00
Sidharth VinodandGitHub ea13b2a54f Merge pull request #1216 from mermaid-js/release-promotion
Release live editor
2023-05-01 12:01:23 +05:30
Sidharth Vinod 448b47a8e3 Fix title 2023-05-01 12:00:53 +05:30
Sidharth Vinod 90407fe0d1 Change branch 2023-05-01 11:59:52 +05:30
Sidharth Vinod 85c3ddd365 change release branch name 2023-05-01 11:57:59 +05:30
Sidharth Vinod 6655a2c320 Update release-pr 2023-05-01 11:56:47 +05:30
Sidharth Vinod 52523ca6c2 Change branch 2023-05-01 11:54:30 +05:30
Sidharth Vinod 68496371f9 Merge branch 'develop' of https://github.com/mermaid-js/mermaid-live-editor into develop
* 'develop' of https://github.com/mermaid-js/mermaid-live-editor:
  build: update Browserslist db
2023-05-01 11:53:10 +05:30
Sidharth Vinod 4994bba729 Release PR 2023-05-01 11:53:05 +05:30
Sidharth VinodandGitHub d8581aca56 Merge pull request #1214 from mermaid-js/browserslist-update
build: update Browserslist db
2023-05-01 06:16:02 +00:00
GitHub Action 2d5aa85b36 build: update Browserslist db 2023-05-01 02:27:53 +00:00
Sidharth VinodandGitHub 94f8f5087f Merge pull request #1208 from mermaid-js/develop
Remove suggestions in editor
2023-04-30 22:13:52 +05:30
Sidharth VinodandGitHub 37085cce55 Merge pull request #1206 from mermaid-js/renovate/patch-all-minor-patch
Update all non-major dependencies (patch)
2023-04-29 17:24:35 +00:00
Sidharth VinodandGitHub e81a6a4eab Merge pull request #1207 from mermaid-js/renovate/all-minor-patch
Update all non-major dependencies (minor)
2023-04-29 17:24:26 +00:00
renovate[bot]andGitHub f7380ab331 Update all non-major dependencies 2023-04-28 15:53:18 +00:00
renovate[bot]andGitHub 72dfdf7a9a Update all non-major dependencies 2023-04-27 07:26:41 +00:00
Sidharth Vinod f209011795 Remove unnecessary completions 2023-04-27 11:31:54 +05:30
Sidharth Vinod ebbd7f252c Merge branch 'master' into develop
* master:
2023-04-25 13:42:48 +05:30
Sidharth Vinod 5f99efa611 Remove suggestions 2023-04-25 13:42:27 +05:30
Sidharth Vinod 677a00f69f Update vite 2023-04-21 23:53:45 +05:30
Sidharth VinodandGitHub 0192b86128 Merge pull request #1204 from mermaid-js/develop
Release
2023-04-20 13:01:38 +05:30
Sidharth Vinod 8e02db8e19 update sk 2023-04-20 12:55:27 +05:30
Sidharth VinodandGitHub 1d8b1221a1 Merge pull request #1205 from mermaid-js/fix/monaco
Fix/monaco
2023-04-20 07:23:36 +00:00
Sidharth Vinod 5a5ec855a9 fix Test 2023-04-20 12:47:48 +05:30
Sidharth Vinod d63e1a0356 Merge branch 'develop' into fix/monaco
* develop:
  Revert "Fix editor"
  Add loading test
  Revert "Fix/monaco (#865)"
  Fix editor
  Fix/monaco (#865)
  Fix editor broken moving between code and config
2023-04-20 12:21:49 +05:30
Sidharth Vinod 79fe5a79cd Properly load monaco workers 2023-04-20 12:20:46 +05:30
Sidharth Vinod ab0bdf1780 Properly load monaco workers 2023-04-20 12:20:31 +05:30
Sidharth VinodandGitHub b798a62d97 Merge branch 'master' into develop 2023-04-19 20:55:27 +05:30
Sidharth Vinod eea17f31dd Merge branch 'develop' of https://github.com/mermaid-js/mermaid-live-editor into develop
* 'develop' of https://github.com/mermaid-js/mermaid-live-editor:
  Revert "Fix/monaco (#865)"
2023-04-19 20:54:04 +05:30
Sidharth Vinod 7887854068 Revert "Fix editor"
This reverts commit 6448cbf19d.
2023-04-19 20:54:00 +05:30
Sidharth Vinod adb82e7da0 Add loading test 2023-04-19 20:53:32 +05:30
Sidharth Vinod 1efb5aa62e Revert "Fix/monaco (#865)"
This reverts commit 6e9453da0e.
2023-04-19 20:41:34 +05:30
Sidharth VinodandGitHub 5348de04b9 Merge pull request #1203 from mermaid-js/develop
Release
2023-04-19 19:49:57 +05:30
Sidharth Vinod 6448cbf19d Fix editor 2023-04-19 19:42:57 +05:30
Sidharth Vinod a928f99165 Merge branch 'master' into develop
* master:
  Fix/monaco (#865)
2023-04-19 19:05:57 +05:30
Sidharth VinodandGitHub 943b45849d Merge pull request #1200 from dlitsman/develop
Fix editor broken moving between code and config
2023-04-19 13:16:53 +00:00
Sidharth VinodGitHubGitHub Actionrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
6e9453da0e Fix/monaco (#865)
* Fix monaco import

* Cleanup monaco

* Test fix

* Test fix

* build: update Browserslist db

* chore(deps): update all non-major dependencies

* Fix editor

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
2023-04-19 18:46:20 +05:30
Sidharth Vinod b8fe37f782 Fix editor 2023-04-19 18:38:32 +05:30
Sidharth Vinod 38975c9a32 Merge branch 'develop' into fix/monaco
* develop: (356 commits)
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  fix Test
  chore(deps): update all non-major dependencies
  chore(deps): update node.js to v19
  chore(deps): update dependency jsdom to v21
  chore(deps): update actions/stale action to v8
  chore(deps): update all non-major dependencies
  Bump d3-color from 3.0.1 to 3.1.0
  Did it really work??
  Reduce timeouts
  Splitup tests
  cacheBust loadSite
  Reduce timeout & retries
  Update typeInEditor
  Fix commenting test
  Fix commenting test
  Fix loadSite ?
  Fix commenting test
  Fix branch test
  ...
2023-04-19 18:33:09 +05:30
github-merge-queue[bot]andGitHub fd189c2dfe Merge pull request #1199 from mermaid-js/browserslist-update
build: update Browserslist db
2023-04-19 06:51:01 +00:00
github-merge-queue[bot]andGitHub 335c19d04e Merge pull request #1201 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-04-19 06:48:43 +00:00
renovate[bot]andGitHub ae0e1059dd chore(deps): update all non-major dependencies 2023-04-18 13:29:28 +00:00
Dmitry Litsman a0ccc8f94a Fix editor broken moving between code and config 2023-04-16 15:07:33 +01:00
GitHub Action 12cbbd3567 build: update Browserslist db 2023-04-15 02:25:15 +00:00
Sidharth VinodandGitHub d3f2b81639 Merge pull request #1197 from mermaid-js/develop
Deps
2023-04-13 17:57:14 +05:30
Sidharth Vinod b770dd46d5 fix Test 2023-04-13 17:51:47 +05:30
Sidharth VinodandGitHub b5ae544932 Merge pull request #1155 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-04-13 12:05:57 +00:00
Sidharth VinodandGitHub 222d1f2f01 Merge branch 'master' into develop 2023-04-13 17:35:33 +05:30
renovate[bot]andGitHub c74d604bac chore(deps): update all non-major dependencies 2023-04-13 12:03:30 +00:00
Sidharth VinodandGitHub d688544fc5 Merge pull request #1196 from mermaid-js/renovate/node-19.x
chore(deps): update node.js to v19
2023-04-13 12:02:27 +00:00
Sidharth VinodandGitHub aeff048783 Merge pull request #1195 from mermaid-js/renovate/jsdom-21.x
chore(deps): update dependency jsdom to v21
2023-04-13 12:02:20 +00:00
Sidharth VinodandGitHub 4b2b36bbae Merge pull request #1194 from mermaid-js/renovate/actions-stale-8.x
chore(deps): update actions/stale action to v8
2023-04-13 12:02:13 +00:00
renovate[bot]andGitHub 6282001d9b chore(deps): update node.js to v19 2023-04-13 12:02:06 +00:00
Sidharth VinodandGitHub 6ad555fbd8 Merge pull request #1170 from mermaid-js/dependabot/npm_and_yarn/d3-color-3.1.0
Bump d3-color from 3.0.1 to 3.1.0
2023-04-13 12:02:03 +00:00
renovate[bot]andGitHub 506ead98fc chore(deps): update dependency jsdom to v21 2023-04-13 12:02:02 +00:00
Sidharth VinodandGitHub 23dba5aaac Merge pull request #1169 from mermaid-js/dependabot/npm_and_yarn/braintree/sanitize-url-6.0.2
Bump @braintree/sanitize-url from 6.0.0 to 6.0.2
2023-04-13 12:01:49 +00:00
renovate[bot]andGitHub 00ebee9438 chore(deps): update actions/stale action to v8 2023-04-13 12:01:46 +00:00
Sidharth VinodandGitHub 07d312c84c Merge pull request #1154 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-04-13 12:00:51 +00:00
Sidharth VinodandGitHub db5506077a Merge pull request #1193 from mermaid-js/develop
10.1.0
2023-04-13 17:30:01 +05:30
renovate[bot]andGitHub 172a9950b4 chore(deps): update all non-major dependencies 2023-04-13 11:54:14 +00:00
dependabot[bot]andGitHub 7e112c76c7 Bump d3-color from 3.0.1 to 3.1.0
Bumps [d3-color](https://github.com/d3/d3-color) from 3.0.1 to 3.1.0.
- [Release notes](https://github.com/d3/d3-color/releases)
- [Commits](https://github.com/d3/d3-color/compare/v3.0.1...v3.1.0)

---
updated-dependencies:
- dependency-name: d3-color
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 11:53:47 +00:00
Sidharth VinodandGitHub a12efbc4e7 Merge pull request #1192 from mermaid-js/release/10.1.0
"mermaid": "10.1.0"
2023-04-13 11:51:27 +00:00
Sidharth Vinod 3febc39331 Did it really work?? 2023-04-13 17:16:31 +05:30
Sidharth Vinod 1daca60eab Reduce timeouts 2023-04-13 17:10:52 +05:30
Sidharth Vinod c43c691070 Splitup tests 2023-04-13 17:09:37 +05:30
Sidharth Vinod 0301aa1a06 cacheBust loadSite 2023-04-13 17:01:57 +05:30
Sidharth Vinod 97472ca368 Reduce timeout & retries 2023-04-13 16:50:47 +05:30
Sidharth Vinod e789adf5f8 Update typeInEditor 2023-04-13 16:44:07 +05:30
Sidharth Vinod c821848b74 Fix commenting test 2023-04-13 16:42:12 +05:30
Sidharth Vinod 6fc44e06ed Fix commenting test 2023-04-13 16:38:32 +05:30
Sidharth Vinod 28452a8b7d Fix loadSite ? 2023-04-13 16:35:31 +05:30
Sidharth Vinod e5a4e7b997 Fix commenting test 2023-04-13 16:31:02 +05:30
Sidharth Vinod d364cdfd83 Fix branch test 2023-04-13 13:00:46 +05:30
Sidharth Vinod 982ef0377f Fix tests 2023-04-13 12:40:09 +05:30
Sidharth Vinod 8a13644b9a Merge branch 'develop' into release/10.1.0
* develop:
  build: update Browserslist db
2023-04-13 12:13:29 +05:30
Sidharth Vinod 5c19c117cf Update other deps 2023-04-13 12:08:01 +05:30
Sidharth VinodandGitHub 46907e5fa3 Merge pull request #1182 from mermaid-js/browserslist-update
build: update Browserslist db
2023-04-13 06:29:29 +00:00
dependabot[bot]andGitHub 61fb92ea68 Bump @braintree/sanitize-url from 6.0.0 to 6.0.2
Bumps [@braintree/sanitize-url](https://github.com/braintree/sanitize-url) from 6.0.0 to 6.0.2.
- [Release notes](https://github.com/braintree/sanitize-url/releases)
- [Changelog](https://github.com/braintree/sanitize-url/blob/main/CHANGELOG.md)
- [Commits](https://github.com/braintree/sanitize-url/compare/v6.0.0...v6.0.2)

---
updated-dependencies:
- dependency-name: "@braintree/sanitize-url"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-13 06:28:47 +00:00
Sidharth Vinod e5afb85239 "mermaid": "10.1.0" 2023-04-13 11:58:34 +05:30
Sidharth Vinod 8edce6a3c1 Revert ""mermaid": "10.1.0""
This reverts commit d4fadaff24.
2023-04-13 11:57:49 +05:30
Sidharth Vinod d4fadaff24 "mermaid": "10.1.0" 2023-04-13 00:00:40 +05:30
GitHub Action 19d7be1058 build: update Browserslist db 2023-04-01 02:25:11 +00:00
Sidharth Vinod bf6c54c00e Merge branch 'master' into develop
* master:
  feat: Add mermaidchart logo
2023-03-16 00:41:56 +05:30
Sidharth Vinod f6344341ef feat: Add mermaidchart logo 2023-03-16 00:39:36 +05:30
Sidharth VinodandGitHub 3874aa48fc Merge pull request #1177 from mermaid-js/browserslist-update
build: update Browserslist db
2023-03-15 11:31:54 +00:00
GitHub Action cb43694063 build: update Browserslist db 2023-03-15 02:33:54 +00:00
Sidharth VinodandGitHub 8215f56eba Merge pull request #1168 from mermaid-js/browserslist-update
build: update Browserslist db
2023-03-03 08:49:58 +00:00
GitHub Action 395e0a558b build: update Browserslist db 2023-03-01 02:54:27 +00:00
Sidharth VinodandGitHub 1605536f13 Merge pull request #1164 from mermaid-js/develop
Release v10
2023-02-24 10:51:07 +05:30
Sidharth VinodandGitHub 7dc59f1396 Merge pull request #1160 from mermaid-js/release/10.0.0
Release/10.0.0
2023-02-24 10:50:07 +05:30
Sidharth Vinod 59168d2437 fix: re enable uncompressed URL test 2023-02-24 10:37:29 +05:30
Sidharth Vinod 5a6dccb263 fix: Comment test caret location 2023-02-24 10:36:20 +05:30
Sidharth Vinod 6c16fa76ff fix: Adjust image size 2023-02-24 10:32:10 +05:30
Sidharth Vinod ccbb45a09c fix: E2e Tests 2023-02-24 10:10:52 +05:30
Sidharth Vinod 052665ef09 Actions, default closed 2023-02-22 10:01:16 +05:30
Sidharth Vinod ee1652e9f2 v10 2023-02-21 17:46:00 +05:30
Sidharth Vinod bbd6378065 v10 changes 2023-02-19 20:09:42 +05:30
Sidharth Vinod e93641e41f Release v10 2023-02-19 19:49:50 +05:30
Sidharth Vinod 5925448ca4 Replace moment with dayjs 2023-02-19 00:14:57 +05:30
Sidharth Vinod 5e930d19e7 Merge remote-tracking branch 'origin/develop' into release/9.4
* origin/develop:
  Fix browserlist base branch
  build: update Browserslist db
  Move browserlist update to cron
  Update Browserslist
  Update snapshots
  Fix Preset
  Update Browserslist
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore: Cleanup with eslint-unicorn
  Fix tests
  Fix tests
  Update loadSite.spec.ts
  Update snapshots.js
  Also changed 'graph' to 'flowchart' on state.ts
  Replacing "chart" with "flowchart"
2023-02-16 21:04:47 +05:30
Sidharth VinodandGitHub 131dd06cde Merge pull request #1158 from mermaid-js/develop
Release 9.4
2023-02-16 21:02:05 +05:30
Sidharth VinodandGitHub e96225dc69 Merge branch 'master' into develop 2023-02-16 18:02:16 +05:30
Sidharth VinodandGitHub 3a42afd102 Merge pull request #1156 from mermaid-js/browserslist-update
build: update Browserslist db
2023-02-16 17:34:26 +05:30
Sidharth Vinod 5394074ac5 Fix browserlist base branch 2023-02-16 17:33:42 +05:30
Sidharth Vinod 20358bc254 Remove mindmap 2023-02-16 15:56:30 +05:30
Sidharth Vinod 6ece0a8a94 comment out consoles 2023-02-16 15:55:07 +05:30
Sidharth Vinod 71f4097fa0 Log SVG code 2023-02-16 15:51:53 +05:30
Sidharth Vinod 308aec1580 Merge branch 'release/9.4' of https://github.com/mermaid-js/mermaid-live-editor into release/9.4
* 'release/9.4' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2023-02-16 15:48:06 +05:30
Sidharth Vinod 8c07649b10 Remove mindmap 2023-02-16 15:48:02 +05:30
sidharthv96 3a1b8f87c3 Update Browserslist 2023-02-16 10:02:52 +00:00
Sidharth Vinod af4f60874e 9.4.0 2023-02-16 15:31:31 +05:30
GitHub Action e5f303e6e2 build: update Browserslist db 2023-02-15 02:44:06 +00:00
Sidharth VinodandGitHub e461f108f1 Merge pull request #1106 from mermaid-js/sidv/eslint-unicorn
Cleanup with eslint-unicorn
2023-02-10 19:25:54 +00:00
Sidharth VinodandGitHub 64519ba8a1 Merge pull request #1153 from mermaid-js/sidv/browserlist
Move browserlist update to cron
2023-02-10 19:09:03 +00:00
Sidharth Vinod ec5937b4fb Move browserlist update to cron 2023-02-11 00:32:39 +05:30
sidharthv96 10a11cfc32 Update Browserslist 2023-02-10 18:53:40 +00:00
Sidharth Vinod 9ebbd3b36c Merge remote-tracking branch 'origin/develop' into sidv/eslint-unicorn
* origin/develop: (51 commits)
  Update snapshots
  Fix Preset
  Update Browserslist
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update dependency eslint to v8.33.0
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update dependency eslint to v8.30.0
  Fix links
  chore(deps): update all non-major dependencies
  SvelteKit 1.0!
  Update sveltekit
  Skip updatebrowserslit on renovate
  v9.3.0
  mermaid 9.3.0-rc.7
  Fix vite.config.js typing
  mermaid 9.3.0-rc.6
  ...
2023-02-11 00:22:23 +05:30
Sidharth VinodandGitHub 9e25c2d2c4 Merge pull request #654 from victorp13/patch-1
Replacing "graph" with "flowchart"
2023-02-08 12:43:51 +05:30
Sidharth Vinod 9853442201 Update snapshots 2023-02-08 00:16:46 +05:30
Sidharth VinodandGitHub ebca0ebe34 Merge pull request #1151 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-02-08 00:15:29 +05:30
Sidharth Vinod abb2405436 Merge branch 'patch-1' of https://github.com/victorp13/mermaid-live-editor into pr/victorp13/654
* 'patch-1' of https://github.com/victorp13/mermaid-live-editor:
  Update Browserslist
2023-02-08 00:09:31 +05:30
Sidharth Vinod a6b1254425 Fix Preset 2023-02-08 00:09:26 +05:30
sidharthv96 95bf41d3e6 Update Browserslist 2023-02-07 18:38:28 +00:00
Sidharth Vinod 68f81df59b Merge remote-tracking branch 'origin/develop' into pr/victorp13/654
* origin/develop: (509 commits)
  chore(deps): update all non-major dependencies
  chore(deps): update dependency eslint to v8.33.0
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  chore(deps): update dependency eslint to v8.30.0
  Fix links
  chore(deps): update all non-major dependencies
  SvelteKit 1.0!
  Update sveltekit
  Skip updatebrowserslit on renovate
  v9.3.0
  mermaid 9.3.0-rc.7
  Fix vite.config.js typing
  mermaid 9.3.0-rc.6
  rc.5
  fix: Adjust image size
  Update to rc.4
  Update to rc.3
  ...
2023-02-08 00:05:14 +05:30
renovate[bot]andGitHub d3b25e5cb8 chore(deps): update all non-major dependencies 2023-02-07 18:30:45 +00:00
Sidharth VinodandGitHub 65c1ea844c Merge pull request #1145 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-02-07 23:59:00 +05:30
renovate[bot]andGitHub 864988e24a chore(deps): update all non-major dependencies 2023-02-07 09:28:56 +00:00
sidharthv96 2fc032f58a Update Browserslist 2023-01-31 16:36:15 +00:00
Sidharth Vinod 2033e9002f 9.4.0-rc.1 2023-01-31 22:04:56 +05:30
Sidharth VinodandGitHub 651a5cb7ef Merge pull request #1146 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency eslint to v8.33.0
2023-01-30 09:20:01 +05:30
renovate[bot]andGitHub a7454aa8cb chore(deps): update dependency eslint to v8.33.0 2023-01-30 02:01:16 +00:00
Sidharth VinodandGitHub 43ff34178e Merge pull request #1142 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-01-29 01:19:45 +05:30
renovate[bot]andGitHub 703744ebf3 chore(deps): update all non-major dependencies 2023-01-27 20:34:14 +00:00
Sidharth VinodandGitHub 15eaee6e9b Merge pull request #1120 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2023-01-21 22:47:07 +05:30
renovate[bot]andGitHub fa9b0344f4 chore(deps): update all non-major dependencies 2023-01-21 14:29:26 +00:00
Sidharth VinodandGitHub 965b39c2f2 Merge pull request #1138 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2023-01-17 21:38:40 +05:30
renovate[bot]andGitHub 26908080cb chore(deps): update all non-major dependencies 2023-01-16 18:05:56 +00:00
Sidharth VinodandGitHub 7c64a65497 Merge pull request #1128 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-12-27 13:06:37 +05:30
renovate[bot]andGitHub be75d950bd chore(deps): update all non-major dependencies 2022-12-26 21:02:04 +00:00
Sidharth VinodandGitHub 4022d8d872 Merge pull request #1127 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency eslint to v8.30.0
2022-12-19 10:24:48 +05:30
renovate[bot]andGitHub ccb294e5df chore(deps): update dependency eslint to v8.30.0 2022-12-19 02:44:15 +00:00
Sidharth Vinod 8d1f3f08f8 Merge branch 'develop' of https://github.com/mermaid-js/mermaid-live-editor into develop
* 'develop' of https://github.com/mermaid-js/mermaid-live-editor:
  chore(deps): update all non-major dependencies
2022-12-17 00:17:50 +05:30
Sidharth Vinod a12e9a3e6d Fix links 2022-12-17 00:17:44 +05:30
Sidharth VinodandGitHub c138e1dc94 Merge pull request #1121 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-12-17 00:15:30 +05:30
renovate[bot]andGitHub 74431cdc08 chore(deps): update all non-major dependencies 2022-12-15 15:09:41 +00:00
Sidharth VinodandGitHub 0812b84487 Merge pull request #1124 from mermaid-js/develop
Svelte Kit 1.0 🥳 🍾
2022-12-15 17:50:45 +05:30
Sidharth Vinod cfc5567980 SvelteKit 1.0! 2022-12-15 17:42:40 +05:30
Sidharth Vinod 096f005938 Merge branch 'develop' of https://github.com/mermaid-js/mermaid-live-editor into develop
* 'develop' of https://github.com/mermaid-js/mermaid-live-editor:
  v9.3.0
  mermaid 9.3.0-rc.7
  Fix vite.config.js typing
  mermaid 9.3.0-rc.6
  rc.5
  fix: Adjust image size
  Update to rc.4
  Update to rc.3
  Remove card snapshot
  Update snapshot
  Update to rc.2
  Update Browserslist
  9.3.0-rc.1
2022-12-15 17:31:41 +05:30
Sidharth VinodandGitHub 37d96f9a87 Merge pull request #1123 from mermaid-js/develop
Release 9.3.0
2022-12-15 17:31:14 +05:30
Sidharth Vinod 8c4b7d52d1 Update sveltekit 2022-12-15 17:30:58 +05:30
Sidharth VinodandGitHub cc7411fef8 Merge pull request #1119 from mermaid-js/release/9.3
9.3.0
2022-12-15 17:23:26 +05:30
Sidharth Vinod 1382504456 Skip updatebrowserslit on renovate 2022-12-15 17:20:39 +05:30
Sidharth Vinod 860039a924 v9.3.0 2022-12-15 17:18:24 +05:30
Sidharth Vinod 33a4cbbf78 mermaid 9.3.0-rc.7 2022-12-15 09:15:35 +05:30
Sidharth Vinod 7be86b5fa7 Fix vite.config.js typing 2022-12-14 10:18:21 +05:30
Sidharth Vinod b835e0448d mermaid 9.3.0-rc.6 2022-12-14 09:46:51 +05:30
Sidharth Vinod 7c5f9f1661 rc.5 2022-12-13 13:46:35 +05:30
Sidharth Vinod 0a34cbe9c7 fix: Adjust image size 2022-12-13 12:19:02 +05:30
Sidharth Vinod a2146da8e1 Update to rc.4 2022-12-13 00:40:18 +05:30
Sidharth Vinod d23de5f9e0 Update to rc.3 2022-12-13 00:24:54 +05:30
Sidharth Vinod 7d18fa6b95 Remove card snapshot 2022-12-13 00:11:03 +05:30
Sidharth Vinod 8dbb011b98 Update snapshot 2022-12-13 00:08:20 +05:30
Sidharth Vinod 4c398ed8dd Merge branch 'release/9.3' of https://github.com/mermaid-js/mermaid-live-editor into release/9.3
* 'release/9.3' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2022-12-12 23:58:04 +05:30
Sidharth Vinod 532e7a6537 Update to rc.2 2022-12-12 23:57:59 +05:30
Sidharth Vinod 049635cf3a Update docs button title 2022-12-12 22:34:09 +05:30
sidharthv96 c06511f11f Update Browserslist 2022-12-09 07:14:05 +00:00
Sidharth Vinod e6adff074a 9.3.0-rc.1 2022-12-09 12:42:48 +05:30
Sidharth VinodandGitHub 55988eb750 Merge pull request #1115 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2022-12-06 23:05:09 +05:30
Sidharth VinodandGitHub e368a20b8d Merge pull request #1116 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-12-06 23:04:45 +05:30
Sidharth VinodandGitHub 3cc56a0348 Merge pull request #1117 from aswinthaikkat/updatereadme
docs: Improve installation steps
2022-12-06 22:34:29 +05:30
aswin 36b7aba2f4 docs: Add dependency install step 2022-12-06 22:33:16 +05:30
aswin 54485ac068 docs: Improve installation steps 2022-12-06 22:22:41 +05:30
renovate[bot] fc6cf1f014 Update Browserslist 2022-12-05 01:28:05 +00:00
renovate[bot] 048f280b8e Update Browserslist 2022-12-05 01:27:32 +00:00
renovate[bot]andGitHub 19f26ce4b9 chore(deps): update all non-major dependencies 2022-12-05 01:26:57 +00:00
renovate[bot]andGitHub 6f115d4ac2 chore(deps): update all non-major dependencies 2022-12-05 01:26:30 +00:00
Sidharth VinodandGitHub 347d669c9c Merge pull request #1109 from mermaid-js/develop
Release
2022-11-30 14:25:43 +05:30
Sidharth VinodandGitHub df7da711a9 Merge branch 'master' into develop 2022-11-30 14:19:16 +05:30
Sidharth VinodandGitHub 282dfcc701 Merge pull request #1114 from lagraham337/develop
Nav: Fixed broken tutorial link
2022-11-30 14:18:24 +05:30
Lauren Graham 998c5f7fd9 Nav: Fixed broken tutorial link 2022-11-29 20:54:26 -08:00
Sidharth VinodandGitHub 6c907c48c7 Merge pull request #1113 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2022-11-28 10:33:29 +05:30
renovate[bot]andGitHub fe981b7646 chore(deps): update all non-major dependencies 2022-11-28 01:03:14 +00:00
Sidharth Vinod e36b1d42e4 chore: Skip uncompressed URL test 2022-11-25 16:15:01 +05:30
Sidharth Vinod a62a65ea05 Try something 2022-11-25 14:33:49 +05:30
Sidharth Vinod d9cdab3528 test: Change test order 2022-11-25 14:28:45 +05:30
Sidharth VinodandGitHub 5b3b0a499e Merge pull request #1108 from mermaid-js/develop
Release
2022-11-25 14:19:21 +05:30
Sidharth VinodandGitHub 1b6a1910ce Merge pull request #1107 from mermaid-js/sidv/betterErrors
Show properly formatted error messages.
2022-11-25 13:32:58 +05:30
Sidharth Vinod f794fa9e0e chore: cleanup 2022-11-25 13:25:20 +05:30
Sidharth Vinod 17ca5f29d0 fix: Error type 2022-11-25 13:23:42 +05:30
Sidharth Vinod b7aae5f94f chore: Bump node 2022-11-25 13:19:12 +05:30
Sidharth Vinod 6888765e37 Merge branch 'master' into sidv/eslint-unicorn
* master:
2022-11-25 13:06:08 +05:30
Sidharth Vinod 25e186158d chore: Cleanup with eslint-unicorn 2022-11-25 13:05:57 +05:30
Sidharth Vinod ffd68c7551 Show good error messages. 2022-11-25 12:11:11 +05:30
Sidharth VinodandGitHub 909b95ba7b Merge pull request #1105 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2022-11-25 11:27:44 +05:30
Sidharth Vinod f4dd01c4fe chore: fix trailing slash 2022-11-25 11:17:30 +05:30
renovate[bot]andGitHub 7508a555d4 chore(deps): update all non-major dependencies 2022-11-24 22:05:13 +00:00
Sidharth VinodandGitHub 4453a04a05 Merge pull request #1104 from mermaid-js/develop
Release
2022-11-24 22:14:04 +05:30
Sidharth Vinod 9d3a33e593 Block only tests 2022-11-24 22:07:14 +05:30
Sidharth Vinod 0378cd4ba6 Merge branch 'sidv/fixEditorHang' into develop
* sidv/fixEditorHang:
  Remove it.only
2022-11-24 22:05:27 +05:30
Sidharth Vinod c518217ba7 Merge branch 'develop' of https://github.com/mermaid-js/mermaid-live-editor into develop
* 'develop' of https://github.com/mermaid-js/mermaid-live-editor:
  fix #1103
  fix: Remove AsyncQueue
  Update Browserslist
2022-11-24 22:04:52 +05:30
Sidharth Vinod c9a2e6bf09 snapshot 2022-11-24 22:04:48 +05:30
Sidharth Vinod f136f7bd58 Remove it.only 2022-11-24 22:04:15 +05:30
Sidharth VinodandGitHub 03d2be91e6 Merge pull request #1099 from mermaid-js/sidv/fixEditorHang
Remove AsyncQueue
2022-11-24 22:03:50 +05:30
Sidharth Vinod 3e6c28f15a fix #1103 2022-11-24 21:56:20 +05:30
Sidharth Vinod 2b800e960d Merge branch 'develop' into sidv/fixEditorHang
* develop:
  chore(deps): update all non-major dependencies
  Update Browserslist
  chore: Update deps
2022-11-24 21:26:47 +05:30
Sidharth VinodandGitHub 06bd4cf176 Merge pull request #1101 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-11-24 21:16:10 +05:30
renovate[bot]andGitHub 42a55e10a6 chore(deps): update all non-major dependencies 2022-11-24 14:57:03 +00:00
Sidharth VinodandGitHub 996db3b198 Merge pull request #1100 from mermaid-js/sidv/updateDeps
chore: Update deps
2022-11-24 20:24:51 +05:30
sidharthv96 7a598f408c Update Browserslist 2022-11-24 10:56:27 +00:00
Sidharth Vinod 924d10aac3 chore: Update deps 2022-11-24 16:25:12 +05:30
Sidharth Vinod 22df217b44 Merge branch 'sidv/fixEditorHang' of https://github.com/mermaid-js/mermaid-live-editor into sidv/fixEditorHang
* 'sidv/fixEditorHang' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2022-11-24 16:20:08 +05:30
Sidharth Vinod 7c4c2a02f9 fix: Remove AsyncQueue 2022-11-24 16:20:01 +05:30
sidharthv96 20c6e9146c Update Browserslist 2022-11-24 10:30:12 +00:00
Sidharth VinodandGitHub afeb649833 Merge pull request #1095 from mermaid-js/develop
Release: Fix editor hang
2022-11-17 13:20:15 +05:30
Sidharth VinodandGitHub fcf53c98c2 Merge pull request #1094 from mermaid-js/sidv/fixEditorHang
Fix #1089: Update monaco-mermaid
2022-11-17 13:19:04 +05:30
Sidharth Vinod 749968e0f5 Fix #1089: Update monaco-mermaid 2022-11-17 11:21:21 +05:30
Sidharth VinodandGitHub ba56f08370 Merge pull request #1091 from mermaid-js/develop
Release
2022-11-15 20:43:45 +05:30
Sidharth VinodandGitHub d92bf85009 Merge pull request #1092 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-11-15 20:33:03 +05:30
renovate[bot]andGitHub 6936dc5755 chore(deps): update all non-major dependencies 2022-11-15 14:50:54 +00:00
Sidharth VinodandGitHub b41ff80d9d Merge pull request #1090 from mermaid-js/sidv/capitalizeSvelteComponents
chore: Capitalize components to be inline with svelte standards
2022-11-15 20:15:21 +05:30
Sidharth Vinod 68ab43ac5d fix lint 2022-11-15 19:59:13 +05:30
Sidharth Vinod 68b589a33a chore: Cleanup, fix theme 2022-11-15 19:51:00 +05:30
Sidharth Vinod 9eb39f31a0 fix: Snapshot, env types 2022-11-15 19:00:46 +05:30
Sidharth Vinod 08eaf725b9 chore: Cleanup potentially problematic code. 2022-11-15 18:57:33 +05:30
Sidharth Vinod 85ad89b915 chore: Cleanup potentially
problematic code.
2022-11-15 18:48:16 +05:30
Sidharth Vinod 60c352a849 Fix casing 2022-11-15 17:05:14 +05:30
Sidharth Vinod b0aa0a9cb2 fix: Type errors 2022-11-15 17:03:25 +05:30
Sidharth Vinod 34dafa13c9 test commit 2022-11-15 16:29:49 +05:30
Sidharth Vinod 684bd708de test commit 2022-11-15 16:29:05 +05:30
Sidharth Vinod d5ee6d4c02 Fix card.test.ts 2022-11-15 16:14:41 +05:30
Sidharth Vinod 233f1822d1 chore: Rename components 2022-11-15 16:04:36 +05:30
Sidharth Vinod 0c3ff0d367 Merge branch 'master' into develop
* master:
2022-11-14 12:01:31 +05:30
Sidharth VinodandGitHub 5bf5c4b0f0 Merge pull request #1086 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-11-14 11:48:39 +05:30
Sidharth VinodandGitHub bea78ae04e Merge pull request #1085 from mermaid-js/renovate/cypress-11.x
chore(deps): update dependency cypress to v11
2022-11-14 11:48:17 +05:30
Sidharth VinodandGitHub f661a14080 Merge pull request #1084 from mermaid-js/renovate/actions-stale-6.x
chore(deps): update actions/stale action to v6
2022-11-14 11:47:56 +05:30
renovate[bot]andGitHub a1ce7238b0 chore(deps): update all non-major dependencies 2022-11-14 01:45:41 +00:00
renovate[bot]andGitHub 393ce10bc2 chore(deps): update dependency cypress to v11 2022-11-10 06:52:03 +00:00
renovate[bot]andGitHub a35e00134e chore(deps): update actions/stale action to v6 2022-11-10 06:51:40 +00:00
Sidharth VinodandGitHub 6f2113f0cc Merge pull request #1082 from mermaid-js/develop
Release 9.2.2
2022-11-10 00:18:32 +05:30
Sidharth VinodandGitHub faa4834949 Merge pull request #1081 from mermaid-js/sidv/9.2.2-rc.2
9.2.2
2022-11-10 00:09:13 +05:30
sidharthv96 6c9e79f21d Update Browserslist 2022-11-09 18:34:49 +00:00
Sidharth Vinod af7fd3e413 Merge branch 'develop' into sidv/9.2.2-rc.2
* develop:
  Update Browserslist
  fix(deps): update all non-major dependencies
2022-11-10 00:03:34 +05:30
Sidharth Vinod dfcfdc5178 v9.2.2 2022-11-09 23:52:23 +05:30
Sidharth VinodandGitHub a5d3e93643 Merge pull request #1078 from mermaid-js/renovate/all-minor-patch
fix(deps): update all non-major dependencies (minor)
2022-11-09 15:16:42 +05:30
sidharthv96 091682548e Update Browserslist 2022-11-09 09:37:48 +00:00
Sidharth Vinod 59d305b4f9 9.2.2-rc.2 2022-11-09 15:06:31 +05:30
renovate[bot] 5f0c25ca3c Update Browserslist 2022-11-07 00:12:23 +00:00
renovate[bot]andGitHub 4a556e592c fix(deps): update all non-major dependencies 2022-11-07 00:11:21 +00:00
Sidharth VinodandGitHub 397bb646ae Merge pull request #1075 from mermaid-js/sidv/spaces
Sidv/spaces
2022-11-03 14:30:15 +05:30
Sidharth Vinod 70d68368d3 Skip errors on non git install 2022-11-03 14:26:26 +05:30
Sidharth Vinod f252ca5728 Add prettier pass 2022-11-03 14:20:55 +05:30
Sidharth Vinod 2fcf2eb262 Add .git-blame-ignore-revs 2022-11-03 14:17:33 +05:30
Sidharth Vinod 0ac606b8fb chore: Tabs -> Spaces
Github PR reviews will be formatted much better.
Inline with mermaid project.
2022-11-03 14:09:36 +05:30
Sidharth Vinod 7b254a67cc Add Funding.yml 2022-11-03 11:28:40 +05:30
Sidharth VinodandGitHub f6f75255b9 Merge pull request #1071 from mermaid-js/develop
Async Mermaid v9.2.0
2022-11-01 22:02:54 +05:30
Sidharth Vinod 8d20a66266 chore: Add loading message 2022-11-01 21:51:27 +05:30
Sidharth VinodandGitHub 8910b15886 Merge pull request #1067 from mermaid-js/sidv/async
Async Mermaid
2022-11-01 21:35:42 +05:30
Sidharth Vinod 0e08d19b6e Merge branch 'develop' into sidv/async
* develop:
2022-11-01 21:30:04 +05:30
Sidharth Vinod 754673f4e2 v9.2.0 2022-11-01 21:29:23 +05:30
Sidharth VinodandGitHub 436a19820e Merge pull request #1070 from mermaid-js/develop
Release
2022-11-01 19:23:24 +05:30
Sidharth Vinod 95c3de9111 Merge branch 'sidv/async' of https://github.com/mermaid-js/mermaid-live-editor into sidv/async
* 'sidv/async' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2022-11-01 19:11:28 +05:30
Sidharth Vinod 67377f0652 Merge branch 'develop' into sidv/async
* develop:
  Update Browserslist
  chore(deps): update all non-major dependencies
  Update Browserslist
  chore(deps): update all non-major dependencies
2022-11-01 19:11:23 +05:30
Sidharth VinodandGitHub 29161c8834 Merge pull request #1069 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-11-01 19:10:12 +05:30
renovate[bot] 0ed6f927ec Update Browserslist 2022-11-01 13:20:24 +00:00
renovate[bot]andGitHub c35bae1769 chore(deps): update all non-major dependencies 2022-11-01 13:19:18 +00:00
Sidharth VinodandGitHub 9e7bdade83 Merge pull request #1068 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2022-11-01 18:48:00 +05:30
sidharthv96 577c26bdf4 Update Browserslist 2022-11-01 13:14:04 +00:00
Sidharth Vinod fd79a65c45 rc10 2022-11-01 18:42:36 +05:30
renovate[bot] aa1811d387 Update Browserslist 2022-10-24 00:26:10 +00:00
renovate[bot]andGitHub 4e8d122437 chore(deps): update all non-major dependencies 2022-10-24 00:25:11 +00:00
Sidharth Vinod ea0aaddc7e Comments 2022-10-20 12:17:06 +05:30
Sidharth Vinod 797bac3370 Comments 2022-10-20 12:16:56 +05:30
Sidharth Vinod 80712d9190 enable Github tests 2022-10-19 18:11:18 +05:30
Sidharth Vinod d952258d0b fix Out of sync tests 2022-10-19 18:05:45 +05:30
Sidharth Vinod e52ad1f650 skip securityLevel test 2022-10-19 17:51:59 +05:30
Sidharth Vinod 6ed2acce75 fix Tests 2022-10-19 17:44:29 +05:30
Sidharth Vinod 4a8875027c fix Tests 2022-10-19 17:08:38 +05:30
Sidharth Vinod a364b5e1bb cleanup 2022-10-19 16:41:08 +05:30
Sidharth Vinod 40c03f7c9f editor flush 2022-10-19 15:58:48 +05:30
Sidharth Vinod 743b1a071c Merge branch 'sidv/async' of https://github.com/mermaid-js/mermaid-live-editor into sidv/async
* 'sidv/async' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2022-10-19 15:51:11 +05:30
Sidharth Vinod 739fdeb987 editor hack 2022-10-19 15:50:31 +05:30
sidharthv96 73d8647a9d Update Browserslist 2022-10-19 09:49:26 +00:00
Sidharth Vinod c967286dab derived 2022-10-19 15:18:00 +05:30
Sidharth Vinod 384e53d02e asyncable 2022-10-19 15:17:52 +05:30
Sidharth Vinod f7b3df9b76 Add tabindex 2022-10-18 14:33:43 +05:30
Sidharth Vinod 36997f6ac1 chore: A11y changes 2022-10-18 14:25:26 +05:30
Sidharth VinodandGitHub cfa60122a6 Merge pull request #1066 from mermaid-js/develop
Release
2022-10-18 11:52:22 +05:30
Sidharth VinodandGitHub ad74b62c23 Merge pull request #1062 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2022-10-18 10:56:52 +05:30
renovate[bot]andGitHub d26e235065 chore(deps): update all non-major dependencies 2022-10-18 05:18:48 +00:00
Sidharth VinodandGitHub 8c149728c5 Merge pull request #1063 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2022-10-18 10:47:46 +05:30
Sidharth VinodandGitHub 6c4196527e Merge pull request #1065 from mermaid-js/sidv/updateFavicon
chore: Update favicon
2022-10-18 10:47:30 +05:30
Sidharth Vinod aff4872ba3 Merge branch 'sidv/updateFavicon' of https://github.com/mermaid-js/mermaid-live-editor into sidv/updateFavicon
* 'sidv/updateFavicon' of https://github.com/mermaid-js/mermaid-live-editor:
  Update Browserslist
2022-10-18 10:39:27 +05:30
Sidharth Vinod 0c06e767a5 fix: favicon size 2022-10-18 10:39:21 +05:30
sidharthv96 3ff15c9437 Update Browserslist 2022-10-18 05:06:35 +00:00
Sidharth Vinod a89eb12b8c chore: Update favicon 2022-10-18 10:35:12 +05:30
renovate[bot] e4000c62e4 Update Browserslist 2022-10-17 00:54:31 +00:00
renovate[bot]andGitHub e02fd80d9a chore(deps): update all non-major dependencies 2022-10-17 00:53:30 +00:00
Sidharth VinodandGitHub 6b5aa2c277 Merge pull request #1059 from mermaid-js/develop
Release
2022-10-10 20:58:18 +08:00
Sidharth VinodandGitHub 4c22921e4e Merge pull request #1056 from mermaid-js/develop
Release
2022-10-07 19:13:21 +08:00
Sidharth Vinod 6167503246 Merge branch 'develop' into fix/monaco
* develop: (23 commits)
  Fix test
  Fix test
  Fix test
  Fix security formatting.
  Fix cypress
  Pin versions; Fix svelte kit
  Cypress
  Remove mocha
  Add dom testing
  Add vitest
  Add check before typing
  Split vite config
  Update kit
  Update Browserslist
  Add license
  chore(deps): bump mermaid from 9.1.2 to 9.1.3 (#877)
  chore(deps-dev): bump lint-staged from 13.0.2 to 13.0.3 (#872)
  Update Browserslist
  chore(deps-dev): bump cypress from 10.2.0 to 10.3.0 (#873)
  chore(deps-dev): bump @sveltejs/kit (#871)
  ...
2022-07-07 11:59:58 +05:30
Sidharth Vinod f7544d0d9a Test fix 2022-06-28 17:59:23 +05:30
Sidharth Vinod 705f257cc1 Test fix 2022-06-28 16:30:22 +05:30
Sidharth Vinod d2b0ae012f Cleanup monaco 2022-06-28 15:51:05 +05:30
Sidharth Vinod 44c6e2848e Fix monaco import 2022-06-28 15:13:08 +05:30
Sidharth Vinod bf06bdf71a Fix tests 2022-02-22 23:05:18 +05:30
Sidharth Vinod 68a0d29320 Fix tests 2022-02-22 22:28:32 +05:30
Victor PikulaandGitHub 14ac5f23a5 Update loadSite.spec.ts 2022-02-22 15:17:57 +01:00
Victor PikulaandGitHub e7676cf4a8 Update snapshots.js 2022-02-22 15:17:19 +01:00
Victor PikulaandGitHub 0f34a76ded Also changed 'graph' to 'flowchart' on state.ts 2022-02-21 15:51:32 +01:00
Victor PikulaandGitHub ee934c60b4 Replacing "chart" with "flowchart"
Since the documentation mentions "flowchart" in the example pages, it makes more sense to also show a flowchart as the first example loaded in the editor. It looks to me like "graph" was deprecated? Not 100% sure.
2022-02-16 09:36:26 +01:00
109 changed files with 9047 additions and 6013 deletions
+10 -1
View File
@@ -2,4 +2,13 @@
**/.git
**/.svelte-kit
**/dist
**/docs
**/docs
**/.github
**/.husky
**/.vscode
Dockerfile
.dockerignore
docker-compose.yml
README.md
+2
View File
@@ -0,0 +1,2 @@
MERMAID_DOMAIN="mermaid.live"
MERMAID_ANALYTICS_URL="https://p.mermaid.live"
+95 -48
View File
@@ -1,50 +1,97 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
'prettier'
],
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint', 'es', 'vitest'],
ignorePatterns: [
'docs/*',
'*.cjs',
'*.js',
'*.md',
'snapshots.js',
'svelte.config.js',
'renovate.json',
'package.json',
'tsconfig.json'
],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
extraFileExtensions: ['.svelte'],
allowAutomaticSingleRunInference: true
},
env: {
browser: true,
es2020: true
},
rules: {
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'es/no-regexp-lookbehind-assertions': 'error',
curly: ['error', 'all']
}
root: true,
parser: '@typescript-eslint/parser',
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'plugin:unicorn/recommended',
'plugin:svelte/recommended',
'plugin:svelte/prettier',
'prettier'
],
plugins: ['tailwindcss', '@typescript-eslint', 'es', 'vitest', 'no-only-tests', 'unicorn'],
ignorePatterns: [
'docs/*',
'*.cjs',
'*.js',
'*.md',
'snapshots.js',
'svelte.config.js',
'renovate.json',
'package.json',
'tsconfig.json'
],
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
},
{
files: ['*.ts'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
'prettier'
]
}
],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
tsconfigRootDir: __dirname,
project: './tsconfig.json',
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2020: true
},
rules: {
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description'
}
],
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'es/no-regexp-lookbehind-assertions': 'error',
curly: ['error', 'all'],
'no-only-tests/no-only-tests': 'error',
'unicorn/no-null': 'off',
'unicorn/filename-case': [
'error',
{
case: 'camelCase'
}
],
'unicorn/filename-case': 'off',
'unicorn/prevent-abbreviations': [
'error',
{
allowList: {
ctx: true,
db: true,
doc: true,
env: true,
fn: true,
i: true,
param: true,
req: true,
res: true,
str: true,
searchParams: true,
temp: true,
ImportMetaEnv: true
}
}
]
}
};
+5
View File
@@ -0,0 +1,5 @@
# .git-blame-ignore-revs
# Prettier Pass
6fde9e22a3ba0b80879a1909fb233367ef20b738
# Tabs -> Spaces
0ac606b8fbf6f94215cbda1d4511e2dcd30e4655
+1
View File
@@ -0,0 +1 @@
github: [sidharthv96, knsv]
+1 -1
View File
@@ -12,6 +12,6 @@ Describe the way your implementation works or what design decisions you made if
Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/master/CONTRIBUTING.md)
- [ ] :book: have read the [contribution guidelines](https://mermaid.js.org/community/contributing.html)
- [ ] :computer: have added unit/e2e tests (if appropriate)
- [ ] :bookmark: targeted `develop` branch
+4 -1
View File
@@ -16,10 +16,13 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: '16'
node-version: 18
cache: yarn
- name: Build & Deploy
env:
MERMAID_DOMAIN: 'mermaid.live'
MERMAID_ANALYTICS_URL: 'https://p.mermaid.live'
run: |
export DEPLOY=true
[ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/
+31 -52
View File
@@ -2,13 +2,11 @@ name: Docker
on:
push:
# Publish `master` as Docker `latest` image.
branches:
# Publish `master` as Docker `latest` image.
- master
# Publish `v1.2.3` tags as releases.
tags:
- v*
# Publish `develop` as Docker `nightly` image.
- develop
# Run tests for all PRs to master and develop.
pull_request:
@@ -16,54 +14,35 @@ on:
- master
- develop
env:
IMAGE_NAME: mermaid-live-editor
jobs:
test:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version
run: |
docker build . --file Dockerfile
push:
# Ensure test job passes before pushing image.
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v3
- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME
- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Push image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
# Change all uppercase to lowercase
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
# Strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
# Strip "v" prefix from tag name
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
# Use Docker `latest` tag convention
[ "$VERSION" == "master" ] && VERSION=latest
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION
RELEASE_VERSION=$([ "${{ github.ref_name }}" = "master" ] && echo "latest" || echo "nightly")
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_ENV}"
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=${{ env.RELEASE_VERSION }}
- uses: docker/build-push-action@v5
with:
context: .
target: mermaid
push: ${{ github.event_name == 'push' }}
pull: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+21
View File
@@ -0,0 +1,21 @@
name: Create release pull request
on:
push:
branches:
- develop
jobs:
productionPromotion:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: master
- name: Reset promotion branch
run: |
git fetch origin develop:develop
git reset --hard develop
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
branch: release-promotion
title: Release live editor
-20
View File
@@ -1,20 +0,0 @@
name: Mark stale issues and pull requests
on:
schedule:
- cron: '0 0 * * 4'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
exempt-issue-labels: 'retained'
exempt-pr-labels: 'retained'
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 30 days'
stale-pr-message: 'This pr is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 30 days'
days-before-stale: 90
days-before-close: 30
days-before-pr-close: -1
+2 -1
View File
@@ -32,7 +32,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
# Install NPM dependencies, cache them correctly
@@ -48,3 +48,4 @@ jobs:
parallel: true
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_CI: true
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
- name: Lint & Test
+27 -13
View File
@@ -1,18 +1,32 @@
name: Update Browserslist
name: Update Browserslist database
on:
push:
branches-ignore:
- 'develop'
- 'master'
schedule:
- cron: '0 2 1,15 * *'
permissions:
contents: write
pull-requests: write
jobs:
build:
update-browserslist-database:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npx browserslist@latest --update-db
- name: Commit changes
uses: EndBug/add-and-commit@v9
- name: Checkout repository
uses: actions/checkout@v3
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: 'Update Browserslist'
fetch-depth: 0
- name: Configure git
run: |
git config --global user.email "action@github.com"
git config --global user.name "GitHub Action"
- name: Update Browserslist database and create PR if applies
uses: c2corg/browserslist-update-action@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: browserslist-update
base_branch: develop
commit_message: 'build: update Browserslist db'
title: 'build: update Browserslist db'
body: Auto-generated by [browserslist-update-action](https://github.com/c2corg/browserslist-update-action/)
labels: 'chores, github action'
@@ -1,25 +0,0 @@
name: Update Monaco-editor
on:
workflow_dispatch:
push:
branches:
- 'dependabot/npm_and_yarn/develop/monaco-editor-**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Update monaco version
run: |
yarn install
node ./bin/update-monaco.js
- name: Commit changes
uses: EndBug/add-and-commit@v9
with:
author_name: ${{ github.actor }}
author_email: ${{ github.actor }}@users.noreply.github.com
message: 'Update Monaco-editor'
+1
View File
@@ -0,0 +1 @@
20.14.0
+7 -6
View File
@@ -1,8 +1,9 @@
{
"singleQuote": true,
"svelteSortOrder": "options-scripts-markup-styles",
"bracketSameLine": true,
"useTabs": true,
"trailingComma": "none",
"printWidth": 100
"singleQuote": true,
"svelteSortOrder": "options-scripts-markup-styles",
"bracketSameLine": true,
"trailingComma": "none",
"printWidth": 100,
"tailwindConfig": "./tailwind.config.cjs",
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"]
}
+13 -13
View File
@@ -1,15 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
+31 -7
View File
@@ -1,9 +1,33 @@
{
"editor.formatOnSave": true,
"cSpell.words": ["pako", "Serde", "serdes"],
"vitest.commandLine": "yarn test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
"svelte.enable-ts-plugin": true,
"githubPullRequests.ignoredPullRequestBranches": ["develop"]
"editor.formatOnSave": true,
"cSpell.blockCheckingWhenLineLengthGreaterThan": 150,
"cSpell.words": [
"asyncable",
"Browserslist",
"ckppp",
"corg",
"cssnano",
"daisyui",
"esserializer",
"gantt",
"gitgraph",
"KROKI",
"localstorage",
"mermaidchart",
"mindmap",
"Pageview",
"pako",
"panzoom",
"pzoom",
"roughjs",
"Serde",
"serdes",
"tailwindcss",
"uparrow"
],
"vitest.commandLine": "yarn test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
"svelte.enable-ts-plugin": true,
"githubPullRequests.ignoredPullRequestBranches": ["develop"]
}
+29 -13
View File
@@ -1,17 +1,33 @@
# Two-stage docker container for mermaid-js/mermaid-live-editor
# Build : docker build -t mermaid-js/mermaid-live-editor .
# Run : docker run --name mermaid-live-editor --publish 8080:80 mermaid-js/mermaid-live-editor
# Start : docker start mermaid-live-editor
# Use webbrowser : http://localhost:8080
# Stop : press ctrl + c
# or
# docker stop mermaid-live-editor
FROM node:18.10.0 as mermaid-live-editor-builder
COPY --chown=node:node . /home
WORKDIR /home
FROM docker.io/library/node:20-alpine3.18 AS mermaid-live-editor-dependencies
RUN apk --no-cache add build-base git python3 && \
rm -rf /var/cache/apk/*
RUN yarn global add node-gyp
WORKDIR /app
COPY ./package.json .
COPY ./yarn.lock .
RUN yarn install
FROM mermaid-live-editor-dependencies AS mermaid-live-editor-builder
ARG MERMAID_RENDERER_URL
ARG MERMAID_KROKI_RENDERER_URL
ARG MERMAID_ANALYTICS_URL
ARG MERMAID_DOMAIN
COPY . ./
RUN yarn build
FROM nginx:alpine as mermaid-live-editor-runner
FROM mermaid-live-editor-builder AS mermaid-dev
ENTRYPOINT ["yarn", "dev"]
FROM nginx:1.25-alpine3.18 AS mermaid
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=mermaid-live-editor-builder --chown=nginx:nginx /home/docs /usr/share/nginx/html
COPY --from=mermaid-live-editor-builder /app/docs /usr/share/nginx/html
-8
View File
@@ -1,8 +0,0 @@
FROM node:18
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN npm install
COPY . .
RUN ls
CMD ["yarn", "dev"]
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 - 2021 Knut Sveidqvist
Copyright (c) 2020 - 2023 Knut Sveidqvist
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+42 -11
View File
@@ -1,4 +1,4 @@
[![Mermaid Live Editor](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/detailed/2ckppp/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/2ckppp/runs) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)[![Netlify Status](https://api.netlify.com/api/v1/badges/27fa023d-7c73-4a3f-9791-b3b657a47100/deploy-status)](https://app.netlify.com/sites/mermaidjs/deploys)
[![Mermaid Live Editor](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/detailed/2ckppp/master&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/2ckppp/runs) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Netlify Status](https://api.netlify.com/api/v1/badges/27fa023d-7c73-4a3f-9791-b3b657a47100/deploy-status)](https://app.netlify.com/sites/mermaidjs/deploys)
# Contributors are welcome!
@@ -24,40 +24,71 @@ You can try out a live version [here](https://mermaid.live/).
### Run published image
```bash
docker run --publish 8000:80 ghcr.io/mermaid-js/mermaid-live-editor
docker run --platform linux/amd64 --publish 8000:8080 ghcr.io/mermaid-js/mermaid-live-editor
```
### To configure renderer URL
When building, Set the Environment variable MERMAID_RENDERER_URL to the rendering service.
When building set the MERMAID_RENDERER_URL build argument to the rendering service.
Default is `https://mermaid.ink`
### To configure Kroki Instance URL
When building, Set the Environment variable MERMAID_KROKI_RENDERER_URL to your Kroki instance.
When building set the MERMAID_KROKI_RENDERER_URL build argument to your Kroki instance.
Default is `https://kroki.io`
### To configure Analytics
When building set the MERMAID_ANALYTICS_URL build argument to your plausible instance, and MERMAID_DOMAIN to your domain.
Default is empty, disabling analytics.
### Development
```bash
docker compose up --build
```
Then open http://localhost:8000
Then open http://localhost:3000
### Building and running images locally
#### Build
```bash
docker build -t mermaid-js/mermaid-live-editor .
```
#### Run
```bash
docker run --detach --name mermaid-live-editor --publish 8080:8080 mermaid-js/mermaid-live-editor
```
Visit: <http://localhost:8080>
#### Stop
```bash
docker stop mermaid-live-editor
```
## Setup
[Volta](https://volta.sh) is used for managing node and yarn versions.
Below link will help you making a copy of the repository in your local system.
This project is set up using [Yarn](https://classic.yarnpkg.com/en/docs/getting-started):
https://docs.github.com/en/get-started/quickstart/fork-a-repo
```
yarn install
```
## Requirements
- [volta](https://volta.sh/) to manage node versions.
- [Node.js](https://nodejs.org/en/). `volta install node`
- [yarn](https://yarnpkg.com/) package manager. `volta install yarn`
## Development
```
```sh
yarn install
yarn dev -- --open
```
+28 -28
View File
@@ -9,37 +9,37 @@ const monacoVersion = packageJson.dependencies['monaco-editor'].replace('^', '')
// fetch monaco sri info from cdnjs api
const cdnjsAPIResp = await fetch(
`https://api.cdnjs.com/libraries/monaco-editor/${monacoVersion}?fields=sri`
`https://api.cdnjs.com/libraries/monaco-editor/${monacoVersion}?fields=sri`
);
if (cdnjsAPIResp.ok) {
const respJson = await cdnjsAPIResp.json();
const htmlPath = path.join('src', 'app.html');
const appHtml = fs
.readFileSync(htmlPath, 'utf8')
// update monaco version of every asset in app.html
.replaceAll(/[0-9.]+\/min\/vs/g, `${monacoVersion}/min/vs`);
const root = parse(appHtml);
const updateIntegrity = (tag, attr) => {
for (const node of root
.getElementsByTagName(tag)
.filter((node) => node.getAttribute(attr)?.includes('monaco-editor'))) {
const file = node.getAttribute(attr).split(`${monacoVersion}/`)[1];
node.setAttribute('integrity', respJson.sri[file]);
}
};
const respJson = await cdnjsAPIResp.json();
const htmlPath = path.join('src', 'app.html');
const appHtml = fs
.readFileSync(htmlPath, 'utf8')
// update monaco version of every asset in app.html
.replaceAll(/[0-9.]+\/min\/vs/g, `${monacoVersion}/min/vs`);
const root = parse(appHtml);
const updateIntegrity = (tag, attr) => {
for (const node of root
.getElementsByTagName(tag)
.filter((node) => node.getAttribute(attr)?.includes('monaco-editor'))) {
const file = node.getAttribute(attr).split(`${monacoVersion}/`)[1];
node.setAttribute('integrity', respJson.sri[file]);
}
};
updateIntegrity('script', 'src');
updateIntegrity('link', 'href');
updateIntegrity('script', 'src');
updateIntegrity('link', 'href');
fs.writeFileSync(
htmlPath,
prettier.format(root.toString(), {
singleQuote: false,
parser: 'html',
bracketSameLine: true,
useTabs: true
})
);
fs.writeFileSync(
htmlPath,
prettier.format(root.toString(), {
singleQuote: false,
parser: 'html',
bracketSameLine: true,
useTabs: true
})
);
} else {
throw Error('Unable to fetch monaco sri data from cdnjs api.');
throw Error('Unable to fetch monaco sri data from cdnjs api.');
}
+33 -31
View File
@@ -1,35 +1,37 @@
import { defineConfig } from 'cypress';
import fs from 'fs';
import { isFileExist, findFiles } from 'cy-verify-downloads';
import path from 'path';
export default defineConfig({
projectId: '2ckppp',
viewportWidth: 1440,
viewportHeight: 768,
snapshotFileName: './cypress/snapshots.js',
defaultCommandTimeout: 16000,
requestTimeout: 16000,
retries: {
runMode: 4,
openMode: 0
},
e2e: {
setupNodeEvents(on, config) {
on('task', {
isFileExist,
findFiles,
deleteFile(path) {
fs.rmSync(path);
return null;
},
readFileMaybe(filename) {
if (fs.existsSync(filename)) {
return fs.readFileSync(filename, 'utf8');
}
return null;
}
});
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/e2e/**/*.spec.ts'
}
projectId: '2ckppp',
viewportWidth: 1440,
viewportHeight: 768,
snapshotFileName: './cypress/snapshots.js',
defaultCommandTimeout: 5000,
requestTimeout: 5000,
retries: {
runMode: 2,
openMode: 0
},
e2e: {
setupNodeEvents(on, config) {
on('task', {
readAndDeleteFile({ fileNamePattern, folder, mode }) {
const fileNameRegex = new RegExp(fileNamePattern);
const files = fs.readdirSync(folder);
const filename = files.find((file) => file.match(fileNameRegex));
const filePath = path.join(folder, filename);
try {
if (mode === 'size') {
return fs.statSync(filePath).size;
}
return fs.readFileSync(filePath, 'utf8');
} finally {
fs.rmSync(filePath);
}
}
});
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/e2e/**/*.spec.ts'
}
});
+8 -8
View File
@@ -1,10 +1,10 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": "off"
},
"env": {
"cypress/globals": true
}
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": "off"
},
"env": {
"cypress/globals": true
}
}
+36 -39
View File
@@ -1,48 +1,45 @@
import { verifyFileSize } from './util';
import { typeInEditor, verifyFileSizeGreaterThan } from './util';
describe('Check actions', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit');
});
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('Actions').click();
});
it('should update markdown code', () => {
cy.get('#markdown')
.invoke('val')
.then((oldText) => {
cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
cy.get('#markdown')
.invoke('val')
.then((newText) => {
expect(oldText).to.not.eq(newText);
});
});
});
it('should update markdown code', () => {
cy.get('#markdown')
.invoke('val')
.then((oldText) => {
typeInEditor('C --> HistoryTest', { bottom: true, newline: true });
cy.get('#markdown')
.invoke('val')
.then((newText) => {
expect(oldText).to.not.eq(newText);
});
});
});
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!!');
});
it.skip('should load gists from URL', () => {
cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
cy.contains('Load Gist').click();
cy.contains('Go shopping!!');
});
it('should download png and svg', () => {
cy.clock(new Date(2022, 0, 1).getTime());
it('should download png and svg', () => {
cy.get(`#downloadPNG`).click();
verifyFileSizeGreaterThan('diagram', 'png', 34_000);
cy.get(`#downloadPNG`).click();
verifyFileSize('diagram', 'png', 21_000);
cy.get(`#downloadSVG`).click();
verifyFileSizeGreaterThan('diagram', 'svg', 10_000);
cy.get(`#downloadSVG`).click();
verifyFileSize('diagram', 'svg', 10_000);
// Verify downloaded file is different for different diagrams
cy.contains('Sample Diagrams').click();
cy.contains('ER').click();
// Verify downloaded file is different for different diagrams
cy.contains('Sample Diagrams').click();
cy.contains('ER Diagram').click();
cy.get(`#downloadPNG`).click();
verifyFileSizeGreaterThan('diagram', 'png', 40_000);
cy.get(`#downloadPNG`).click();
verifyFileSize('diagram', 'png', 46_000);
cy.get(`#downloadSVG`).click();
verifyFileSize('diagram', 'svg', 12_000);
cy.clock().invoke('restore');
});
cy.get(`#downloadSVG`).click();
verifyFileSizeGreaterThan('diagram', 'svg', 11_000);
});
});
+103 -74
View File
@@ -1,86 +1,115 @@
import { getEditor, cmd } from './util';
import { typeInEditor, cmd } from './util';
describe('Auto sync tests', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
});
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
cy.url().should('contain', '/edit#pako');
cy.window().should('have.property', 'editorLoaded', true);
});
it('should dim diagram when code is edited', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
cy.get('#view').should('not.contain.text', 'Diagram out of sync.');
getEditor().type(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#view').should('contain.text', 'Diagram out of sync.');
cy.getLocalStorage('codeStore').snapshot();
});
it('should dim diagram when code is edited', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
cy.get('#errorContainer').should('not.exist');
typeInEditor(' C --> Test', { bottom: true });
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
cy.getLocalStorage('codeStore').snapshot();
});
it('should update diagram when shortcut is used', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
getEditor().type(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#view').should('contain.text', 'Diagram out of sync.');
getEditor().type(`${cmd}{enter}`);
cy.get('#view').should('not.contain.text', 'Diagram out of sync.');
cy.get('#view').should('not.have.class', 'outOfSync');
});
it('should update diagram when shortcut is used', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
typeInEditor(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#errorContainer').should('contain.text', 'Diagram out of sync.');
typeInEditor(`${cmd}{enter}`);
cy.get('#errorContainer').should('not.exist');
cy.get('#view').should('not.have.class', 'outOfSync');
});
it('should show/hide sync button with auto sync', () => {
cy.get('[data-cy=sync]').should('not.exist');
cy.contains('Auto sync').click();
cy.get('[data-cy=sync]').should('exist');
cy.get('#autoSync').check();
cy.get('[data-cy=sync]').should('not.exist');
});
it('should show/hide sync button with auto sync', () => {
cy.get('[data-cy=sync]').should('not.exist');
cy.contains('Auto sync').click();
cy.get('[data-cy=sync]').should('exist');
cy.get('#autoSync').check();
cy.get('[data-cy=sync]').should('not.exist');
});
it('should not dim diagram when code is in sync', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
getEditor().type(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('[data-cy=sync]').click();
cy.get('#view').should('not.have.class', 'outOfSync');
cy.get('#autoSync').check();
getEditor().type('ing');
cy.get('#view').should('not.have.class', 'outOfSync');
cy.getLocalStorage('codeStore').snapshot();
});
it('should not dim diagram when code is in sync', () => {
cy.contains('Auto sync').click();
cy.get('#view').should('not.have.class', 'outOfSync');
typeInEditor(' C --> Test');
cy.get('#view').should('have.class', 'outOfSync');
cy.get('[data-cy=sync]').click();
cy.get('#view').should('not.have.class', 'outOfSync');
cy.get('#autoSync').check();
typeInEditor('ing');
cy.get('#view').should('not.have.class', 'outOfSync');
cy.getLocalStorage('codeStore').snapshot();
});
it('supports commenting code out/in', () => {
getEditor().type(`{uparrow}${cmd}/`);
cy.get('#view').contains('Car').should('not.exist');
it('should automatically defer rendering when complex diagrams are edited', () => {
cy.get('#view').should('not.have.class', 'outOfSync');
typeInEditor(`
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i
A & B & C & D & E --> F & G & K & Z & i`);
cy.get('#view').should('have.class', 'outOfSync');
cy.get('#errorContainer').should('contain.text', 'It will be updated automatically.');
// The class should be removed automatically after 1 second.
cy.get('#view').should('not.have.class', 'outOfSync');
});
getEditor().type(`{uparrow}${cmd}/`);
cy.get('#view').contains('Car').should('exist');
});
it('supports commenting code out/in', () => {
cy.get('#editor').contains('Car').click({ force: true });
cy.get('#editor').get('textarea').type(`${cmd}/`, { force: true });
cy.get('#view').contains('Car').should('not.exist');
it('supports editing code when code is incorrect', () => {
cy.visit(
'/edit#pako:eNpljjEKwzAMRa8SNOcEnlt6gK5eVFvYJsgOqkwpIXevg9smEE1PnyfxF3DFExgISW-CczQ2D21cYU7a-SGYXRwyvTp9jUhuKlVP-eHy7zA-leQsMEmg_QOM0BLG5FujZVMsaCQmC6ahR5ks2Lw2r84ela4-aREwKpVGwKrl_s7ut3fnkjAIcg_XDzuaUhs'
);
cy.get('#errorContainer').should('not.exist');
getEditor({ newline: true }).type(`branch test`);
cy.get('#editor').contains('branch test').should('exist');
cy.get('#errorContainer')
.contains(
'Error: Trying to checkout branch which is not yet created. (Help try using "branch master")'
)
.should('exist');
});
typeInEditor(`{uparrow}${cmd}/`);
cy.get('#view').contains('Car').should('exist');
});
it('supports editing code when code is incorrect', () => {
cy.visit(
'/edit#pako:eNpljjEKwzAMRa8SNOcEnlt6gK5eVFvYJsgOqkwpIXevg9smEE1PnyfxF3DFExgISW-CczQ2D21cYU7a-SGYXRwyvTp9jUhuKlVP-eHy7zA-leQsMEmg_QOM0BLG5FujZVMsaCQmC6ahR5ks2Lw2r84ela4-aREwKpVGwKrl_s7ut3fnkjAIcg_XDzuaUhs'
);
cy.get('#errorContainer').should('not.exist');
typeInEditor(`branch test`, { bottom: true, newline: true });
cy.get('#editor').contains('branch test').should('exist');
cy.get('#errorContainer')
.contains(
'Error: Trying to checkout branch which is not yet created. (Help try using "branch master")'
)
.should('exist');
});
it('should update diagram after entire text is removed', () => {
// https://github.com/mermaid-js/mermaid-live-editor/issues/1102
typeInEditor(`${cmd} a {backspace}`);
typeInEditor('graph LR');
typeInEditor(' {enter} A-->Car');
cy.get('#view').contains('Car').should('exist');
});
});
describe.only('Pan and Zoom', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
});
it('should toggle pan and zoom', () => {
cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
cy.contains('Pan & Zoom').click();
cy.get('#svg-pan-zoom-reset-pan-zoom').should('exist');
cy.contains('Pan & Zoom').click();
cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
});
describe('Pan and Zoom', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
});
it('should toggle pan and zoom', () => {
cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
cy.contains('Pan & Zoom').click();
cy.get('#svg-pan-zoom-reset-pan-zoom').should('exist');
cy.contains('Pan & Zoom').click();
cy.get('#svg-pan-zoom-reset-pan-zoom').should('not.exist');
});
});
+27 -27
View File
@@ -1,36 +1,36 @@
describe('Editor docs tests', () => {
beforeEach(() => {
cy.on('uncaught:exception', () => {
return false;
});
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('Sample Diagrams').click();
});
beforeEach(() => {
cy.on('uncaught:exception', () => {
return false;
});
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('Sample Diagrams').click();
});
it('Test default loading', () => {
cy.get(`[data-cy=docs][href^="https://mermaid-js.github.io/mermaid"]`).should('exist');
});
it('Test default loading', () => {
cy.get(`[data-cy=docs][href^="https://mermaid.js.org/"]`).should('exist');
});
it('Test to see if the correct URL loads when changing from one diagram to other', () => {
cy.contains('Flow Chart').click();
cy.get(`[data-cy=docs][href$="/#/flowchart"]`).should('exist');
it('Test to see if the correct URL loads when changing from one diagram to other', () => {
cy.contains('Flow').click();
cy.get(`[data-cy=docs][href$="/syntax/flowchart.html"]`).should('exist');
cy.contains('Config').click();
cy.get(`[data-cy=docs][href$="/#/flowchart?id=configuration"]`).should('exist');
cy.contains('Config').click();
cy.get(`[data-cy=docs][href$="/syntax/flowchart.html#configuration"]`).should('exist');
cy.contains('Sequence Diagram').click();
cy.get(`[data-cy=docs][href$="/#/sequenceDiagram?id=configuration"]`).should('exist');
cy.contains('Sequence').click();
cy.get(`[data-cy=docs][href$="/syntax/sequenceDiagram.html#configuration"]`).should('exist');
cy.contains('Code').click();
cy.get(`[data-cy=docs][href$="/#/sequenceDiagram"]`).should('exist');
});
cy.contains('Code').click();
cy.get(`[data-cy=docs][href$="/syntax/sequenceDiagram.html"]`).should('exist');
});
it("Test to check URLs for a case where config URL doesn't exist", () => {
cy.contains('State Diagram').click();
cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
it("Test to check URLs for a case where config URL doesn't exist", () => {
cy.contains('State').click();
cy.get(`[data-cy=docs][href$="/syntax/stateDiagram.html"]`).should('exist');
cy.contains('Config').click();
cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
});
cy.contains('Config').click();
cy.get(`[data-cy=docs][href$="/syntax/stateDiagram.html"]`).should('exist');
});
});
+92 -94
View File
@@ -1,105 +1,103 @@
import { getEditor, verifyFileSnapshot } from './util';
import { typeInEditor, verifyFileSnapshot } from './util';
describe('Save History', () => {
beforeEach(() => {
cy.clock(new Date(2022, 0, 1).getTime());
cy.clearLocalStorage();
cy.visit('/edit');
beforeEach(() => {
cy.clock(new Date(2022, 0, 1).getTime());
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('Actions').click();
cy.contains('History').click();
});
cy.contains('History').click();
});
afterEach(() => {
cy.clock().invoke('restore');
});
afterEach(() => {
cy.clock().invoke('restore');
});
it('should load history from localstorage', () => {
cy.setLocalStorage(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","id":"d7ea820e-21dd-418a-b984-fd58acde09df","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"b749ffc6-522b-4a44-86cf-7c1ffc3146b3","name":"helpful-ocean"}]'
);
cy.setLocalStorage(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","id":"69ea820e-522b-4a44-86cf-fd58acde09df","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
);
cy.reload();
cy.contains('Actions').click();
cy.contains('History').click();
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('helpful-ocean');
cy.get('#historyList').contains('hollow-art');
cy.contains('Restore').click();
cy.contains('Halloween');
cy.contains('Timeline').click();
it('should load history from localstorage', () => {
cy.setLocalStorage(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","id":"d7ea820e-21dd-418a-b984-fd58acde09df","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"b749ffc6-522b-4a44-86cf-7c1ffc3146b3","name":"helpful-ocean"}]'
);
cy.setLocalStorage(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","id":"69ea820e-522b-4a44-86cf-fd58acde09df","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","id":"x749ffc6-21dd-418a-b984-7c1ffc3146b3","name":"needy-mosquito"}]'
);
cy.reload();
cy.contains('History').click();
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('helpful-ocean');
cy.get('#historyList').contains('hollow-art');
cy.contains('Restore').click();
cy.contains('Halloween');
cy.contains('Timeline').click();
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('needy-mosquito');
cy.get('#historyList').contains('barking-dog');
cy.contains('Restore').click();
cy.contains('New Year');
});
cy.get('#historyList').find('li').should('have.length', 2);
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').contains('needy-mosquito');
cy.get('#historyList').contains('barking-dog');
cy.contains('Restore').click();
cy.contains('New Year');
});
it('should save when clicked', () => {
cy.get('#historyList').find('li').should('have.length', 0);
cy.get('#historyList').contains('No items in History');
cy.get('#saveHistory').click();
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').find('li').should('have.length', 1);
cy.get('#saveHistory').click();
cy.on('window:alert', (str) => {
expect(str).to.equal('State already saved.');
});
cy.on('window:confirm', () => true);
getEditor().type(' C --> HistoryTest');
cy.get('#saveHistory').click();
cy.get('#historyList').find('li').should('have.length', 2);
});
it('should save when clicked', () => {
cy.get('#historyList').find('li').should('have.length', 0);
cy.get('#historyList').contains('No items in History');
cy.get('#saveHistory').click();
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').find('li').should('have.length', 1);
cy.get('#saveHistory').click();
cy.on('window:alert', (str) => {
expect(str).to.equal('State already saved.');
});
cy.on('window:confirm', () => true);
typeInEditor(' C --> HistoryTest');
cy.get('#saveHistory').click();
cy.get('#historyList').find('li').should('have.length', 2);
});
it('should be able to restore and delete', () => {
cy.get('#saveHistory').click();
getEditor().type(' C --> HistoryTest');
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').find('li').should('have.length', 1);
cy.contains('HistoryTest');
cy.contains('Restore').click();
cy.contains('HistoryTest').should('not.exist');
cy.contains('Delete').click();
cy.get('#historyList').find('li').should('have.length', 0);
cy.get('#historyList').contains('No items in History');
cy.get('#saveHistory').click();
getEditor().type(' C --> HistoryTest');
cy.get('#saveHistory').click();
cy.get('#editor').type('ing');
cy.get('#clearHistory').click();
cy.on('window:alert', (str) => {
expect(str).to.equal('Clear all saved items?');
});
cy.on('window:confirm', () => true);
cy.get('#historyList').contains('No items in History');
});
it('should be able to restore and delete', () => {
cy.get('#saveHistory').click();
typeInEditor(' C --> HistoryTest');
cy.get('#historyList').find('No items in History').should('not.exist');
cy.get('#historyList').find('li').should('have.length', 1);
cy.contains('HistoryTest');
cy.contains('Restore').click();
cy.contains('HistoryTest').should('not.exist');
cy.contains('Delete').click();
cy.get('#historyList').find('li').should('have.length', 0);
cy.get('#historyList').contains('No items in History');
cy.get('#saveHistory').click();
typeInEditor(' C --> HistoryTest');
cy.get('#saveHistory').click();
cy.get('#editor').type('ing');
cy.get('#clearHistory').click();
cy.on('window:alert', (str) => {
expect(str).to.equal('Clear all saved items?');
});
cy.on('window:confirm', () => true);
cy.get('#historyList').contains('No items in History');
});
// TODO: Fix #639
xit('should auto save history', () => {
getEditor().type(' C --> HistoryTest');
cy.tick(70000);
cy.contains('Timeline').click();
cy.get('#historyList').find('li').should('have.length', 1);
cy.get('#editor').type('ing');
cy.tick(70000);
cy.get('#historyList').find('li').should('have.length', 2);
for (let i = 0; i < 31; i++) {
cy.get('#editor').type('.');
cy.tick(70000);
}
cy.get('#historyList').find('li').should('have.length', 30);
});
// TODO: Fix #639
xit('should auto save history', () => {
typeInEditor(' C --> HistoryTest');
cy.tick(70_000);
cy.contains('Timeline').click();
cy.get('#historyList').find('li').should('have.length', 1);
cy.get('#editor').type('ing');
cy.tick(70_000);
cy.get('#historyList').find('li').should('have.length', 2);
for (let i = 0; i < 31; i++) {
cy.get('#editor').type('.');
cy.tick(70_000);
}
cy.get('#historyList').find('li').should('have.length', 30);
});
it('should download history', () => {
cy.get('#saveHistory').click();
cy.get(`#downloadHistory`).click();
verifyFileSnapshot('history', 'json', 'A[Christmas] -->|Get money| B(Go shopping)');
});
it('should download history', () => {
cy.get('#saveHistory').click();
cy.get(`#downloadHistory`).click();
verifyFileSnapshot('history', 'json', 'A[Christmas] -->|Get money| B(Go shopping)');
});
});
+122 -104
View File
@@ -1,118 +1,136 @@
import { toBase64 } from 'js-base64';
describe('Site Loads', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
});
it('Check Home page load', () => {
cy.url().should('include', '/edit');
cy.contains('History').click();
cy.getLocalStorage('codeStore').snapshot();
});
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
cy.url().should('include', '/edit#pako');
});
it('Check Redirect from old URL', () => {
cy.visit(
'/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
);
cy.url().should('include', '/edit#pako:eNp');
});
it('Check Home page load', () => {
cy.url().should('include', '/edit');
cy.contains('History').click();
cy.getLocalStorage('codeStore').snapshot();
});
it('should load sample diagrams when clicked', () => {
cy.contains('Sample Diagrams').click();
cy.contains('Pie Chart').click();
cy.contains('pie title Pets adopted by volunteers');
cy.contains('Class Diagram').click();
cy.contains('classDiagram');
});
it('should keep code after reload', () => {
cy.get('#editor').contains('Car');
cy.reload();
cy.get('#editor').contains('Car');
});
it('should load diagram from gist', () => {
cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
cy.contains('History').click();
cy.contains('Go shopping!!');
cy.contains('Revisions');
cy.contains('sidharthv96 v8f8f1e2');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
it('Check Redirect from old URL', () => {
cy.visit(
'/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
);
cy.url().should('include', '/edit#pako:eNp');
});
it('should load diagram from gist revision', () => {
cy.visit(
'/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
);
cy.contains('History').click();
cy.contains('Party');
cy.contains('Revisions');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
it('should load sample diagrams when clicked', () => {
cy.contains('Sample Diagrams').click();
cy.contains('Pie').click();
cy.contains('pie title Pets adopted by volunteers');
cy.contains('Class').click();
cy.contains('classDiagram');
});
it('should load diagram from raw files', () => {
cy.visit(
'/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();
});
(Cypress.env('CI') === 'true' ? describe : describe.skip)('github', () => {
it('should load diagram from gist', () => {
cy.visit(`/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a`);
cy.contains('History').click();
cy.contains('Go shopping!!');
cy.contains('Revisions');
cy.contains('sidharthv96 v8f8f1e2');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
// Disabled temporarily. Should be enabled after the issue is fixed in Mermaid.
// it('should prevent setting the "securityLevel" option via URL', () => {
// const b64State = toBase64(
// `{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
// true
// );
// cy.on('window:confirm', () => true);
// cy.visit(`/edit#${b64State}`);
// cy.contains('Config').click();
// cy.contains('forest');
// cy.contains('securityLevel').should('not.exist');
// cy.get('#view').find('img').should('not.exist');
// cy.get('#view').contains('<img');
// cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
// });
it('should load diagram from gist revision', () => {
cy.visit(
'/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19'
);
cy.contains('History').click();
cy.contains('Party');
cy.contains('Revisions');
cy.contains('sidharthv96 v7851e19');
cy.getLocalStorage('codeStore').snapshot();
});
it('should allow persisting "securityLevel" using confirm dialogue', () => {
const b64State = toBase64(
`{"code":"graph TD\\nA[\\"<img src='https://dummyimage.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
true
);
cy.on('window:confirm', () => false);
cy.visit(`/edit#${b64State}`);
cy.contains('Config').click();
cy.contains('forest');
cy.contains('securityLevel');
cy.get('#view').find('img').should('be.visible');
});
it('should load diagram from raw files', () => {
cy.visit(
'/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();
});
});
it('should show troubleshooting steps if loading fails', () => {
cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
cy.reload(true);
cy.contains('Please Click here to Raise an issue in github.');
});
// Disabled temporarily. Should be enabled after the issue is fixed in Mermaid.
// it('should prevent setting the "securityLevel" option via URL', () => {
// const b64State = toBase64(
// `{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64 />\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
// true
// );
// cy.on('window:confirm', () => true);
// cy.visit(`/edit#${b64State}`);
// cy.contains('Config').click();
// cy.contains('forest');
// cy.contains('securityLevel').should('not.exist');
// cy.get('#view').find('img').should('not.exist');
// cy.get('#view').contains('<img');
// cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
// });
it('should load uncompressed URL', () => {
cy.visit(
'/edit/#eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
);
cy.contains('New Year');
cy.visit(
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
);
cy.contains('Animal');
cy.visit(
'/edit/#base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
);
cy.contains('New Year');
});
it.skip('should allow persisting "securityLevel" using confirm dialogue', () => {
const b64State = toBase64(
`{"code":"graph TD\\nA[\\"<img src='https://dummyimage.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","autoSync":true,"updateDiagram":true}`,
true
);
cy.on('window:confirm', () => false);
cy.visit(`/edit#${b64State}`);
cy.get('#editor').type(' ');
cy.contains('Config').click();
cy.contains('forest');
cy.contains('securityLevel');
cy.get('#view').find('img').should('be.visible');
});
it('should load compressed URL', () => {
cy.visit(
'/edit#pako:eNpVkM2KwkAQhF-l6dMK5gVyEDRxvYi7sF6WjIcm0zqDzg_jBJEk725Hd2G3Tw31VVFUj23QjCWeEkUD-1p5kFs2O77BN1M6QFEshg1ncMHzfYDV2ybA1YQYrT_NXvhqgqDqtxPGkI315_ElVU__h-cB6mZLMYd4-Kvsb2GAdWM_jcT_V0xicb03RyqPVLSUoJI-OEfHyZHV0rqfDAqzYccKS3k1pbNC5Ufhuqgp81rbHBJKxuXKc6Quh6-7b7HMqeNfqLYkC7gfanwAlW1ZvQ'
);
cy.contains('New Year');
cy.visit(
'/edit#pako:eNptkU1PwzAMhv9K5BOI9Q9EXBDbJA477YYqITcxndV8QD40weh_Jy1rGR0-OY_tV2_sEyivCSQogzGuGduAtnaixINji0bcf1WVWGfVXdMtx8M1faYm4B8sxR27JLClJd6nwK4VLTlN4bI4jMQd2pLe3C4KFhNNcLQ92jv9ADGLNoTdozc-zIV4ZDsNlud7RtVN7_5Sb_jYrFcN3iN_0pPbEqUZK3QbTP_Ojyv4NdR4bwTHlyMbPcOQ3WJ2CliBpWCRdbnLqFJDOpClGmRJNYauhtr1pS-_6bKMjebkA8hXNJFWgDn5_YdTIFPINDWdb3vu6r8BaWOZRQ'
);
cy.contains('Animal');
});
it('should show troubleshooting steps if loading fails', () => {
cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
cy.reload(true);
cy.contains('Please Click here to Raise an issue in github.');
});
});
describe('Verify types of URLs', () => {
it('should load compressed URL', () => {
cy.visit(
'/edit#pako:eNpVkM2KwkAQhF-l6dMK5gVyEDRxvYi7sF6WjIcm0zqDzg_jBJEk725Hd2G3Tw31VVFUj23QjCWeEkUD-1p5kFs2O77BN1M6QFEshg1ncMHzfYDV2ybA1YQYrT_NXvhqgqDqtxPGkI315_ElVU__h-cB6mZLMYd4-Kvsb2GAdWM_jcT_V0xicb03RyqPVLSUoJI-OEfHyZHV0rqfDAqzYccKS3k1pbNC5Ufhuqgp81rbHBJKxuXKc6Quh6-7b7HMqeNfqLYkC7gfanwAlW1ZvQ'
);
cy.contains('New Year');
cy.visit(
'/edit#pako:eNptkU1PwzAMhv9K5BOI9Q9EXBDbJA477YYqITcxndV8QD40weh_Jy1rGR0-OY_tV2_sEyivCSQogzGuGduAtnaixINji0bcf1WVWGfVXdMtx8M1faYm4B8sxR27JLClJd6nwK4VLTlN4bI4jMQd2pLe3C4KFhNNcLQ92jv9ADGLNoTdozc-zIV4ZDsNlud7RtVN7_5Sb_jYrFcN3iN_0pPbEqUZK3QbTP_Ojyv4NdR4bwTHlyMbPcOQ3WJ2CliBpWCRdbnLqFJDOpClGmRJNYauhtr1pS-_6bKMjebkA8hXNJFWgDn5_YdTIFPINDWdb3vu6r8BaWOZRQ'
);
cy.contains('Animal');
});
describe('Uncompressed URLs', () => {
it('should load URL without specifier', () => {
cy.visit(
'/edit/#eyJjb2RlIjoiZ3JhcGhcbiAgICBUZXN0TGFiZWwiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
);
cy.contains('TestLabel');
cy.visit(
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
);
cy.contains('Animal');
});
it('should load URL with "base64" specifier', () => {
cy.visit(
'/edit/#base64:eyJjb2RlIjoiZ3JhcGhcbiAgICBUZXN0TGFiZWwiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGVmYXVsdFwiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
);
cy.contains('TestLabel');
});
});
});
+50 -50
View File
@@ -1,56 +1,56 @@
describe('Test themes', () => {
describe('Test light themes', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: false
});
}
});
cy.contains('Change Theme').click();
});
describe('Test light themes', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: false
});
}
});
cy.contains('Theme').click();
});
it('should set light theme as default', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
it('should set light theme as default', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
it('should change themes when clicked', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('cupcake').click();
cy.contains('cupcake').parent().should('have.class', 'bordered');
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
it('should change themes when clicked', () => {
cy.contains('light').parent().should('have.class', 'bordered');
cy.contains('cupcake').click();
cy.contains('cupcake').parent().should('have.class', 'bordered');
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('not.have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
describe('Test dark mode', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: true
});
}
});
cy.contains('Change Theme').click();
});
describe('Test dark mode', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit', {
onBeforeLoad(win) {
cy.stub(win, 'matchMedia')
.callThrough()
.withArgs('(prefers-color-scheme: dark)')
.returns({
matches: true
});
}
});
cy.contains('Theme').click();
});
it('should set dark theme as default', () => {
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
it('should set dark theme as default', () => {
cy.contains('light').parent().should('not.have.class', 'bordered');
cy.contains('dark').parent().should('have.class', 'bordered');
cy.getLocalStorage('themeStore').snapshot();
});
});
});
+44 -30
View File
@@ -1,41 +1,55 @@
export const cmd = `{${Cypress.platform === 'darwin' ? 'meta' : 'ctrl'}}`;
export const getEditor = ({ bottom = true, newline = false } = {}) =>
cy
.get('#editor textarea:first')
.click()
.focused()
.type(`${bottom ? '{pageDown}' : cmd}`)
.type(`${newline ? '{enter}' : cmd}`);
interface EditorOptions {
bottom?: boolean;
newline?: boolean;
}
export const typeInEditor = (
text: string,
{ bottom = true, newline = false }: EditorOptions = {}
) => {
cy.window().should('have.property', 'editorLoaded', true);
cy.get('#editor').click();
cy.get('#editor').within(() => {
if (bottom) {
cy.get('textarea').type('{pageDown}', { force: true });
}
if (newline) {
cy.get('textarea').type('{enter}', { force: true });
}
cy.get('textarea').type(text, { force: true });
});
};
const downloadsFolder = Cypress.config('downloadsFolder');
export const verifyFileSize = (
fileType: 'history' | 'diagram',
extension: string,
size: number
export const verifyFileSizeGreaterThan = (
fileType: 'history' | 'diagram',
extension: string,
size: number
) => {
const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
const filePath = `${downloadsFolder}/${fileName}`;
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
cy.task('deleteFile', filePath);
cy.get('#view').should('not.have.class', 'outOfSync');
cy.task('readAndDeleteFile', {
folder: downloadsFolder,
fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`,
mode: 'size'
}).then((fileSize: number) => {
expect(fileSize).to.be.gt(size);
expect(fileSize).to.be.lt(size * 1.3);
});
};
export const verifyFileSnapshot = (
fileType: 'history' | 'diagram',
extension: string,
content: string
fileType: 'history' | 'diagram',
extension: string,
content: string
) => {
const fileName = `mermaid-${fileType}-2022-01-01-000000.${extension}`;
const filePath = `${downloadsFolder}/${fileName}`;
cy.verifyDownload(fileName);
cy.readFile(filePath, null, {
log: false
}).then((buffer) =>
expect(new TextDecoder('utf-8').decode(buffer as ArrayBuffer)).to.contain(content)
);
cy.task('deleteFile', filePath);
cy.task('readAndDeleteFile', {
folder: downloadsFolder,
fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`,
mode: 'content'
}).then((fileContent: number) => {
expect(fileContent).to.contain(content);
});
};
+3 -3
View File
@@ -1,5 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
+4 -4
View File
@@ -1,7 +1,7 @@
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}\",\"autoSync\":true,\"updateDiagram\":true}"
"1": "{\"code\":\"flowchart 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}\",\"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}\",\"autoSync\":true,\"updateDiagram\":true}"
@@ -16,13 +16,13 @@ module.exports = {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"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": "10.6.0",
"__version": "12.17.4",
"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}\",\"autoSync\":false,\"updateDiagram\":false}"
"1": "{\"code\":\"flowchart 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}\",\"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}\",\"autoSync\":true,\"updateDiagram\":false}"
"1": "{\"code\":\"flowchart 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}\",\"autoSync\":true,\"updateDiagram\":false}"
}
},
"Test themes": {
+6 -6
View File
@@ -27,12 +27,12 @@ import { register } from '@cypress/snapshot';
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
register();
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
snapshot(): void;
}
}
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
snapshot(): void;
}
}
}
import 'cypress-localstorage-commands';
+4 -5
View File
@@ -15,14 +15,13 @@
// Import commands.js using ES2015 syntax:
import './commands';
require('cy-verify-downloads').addCustomCommand();
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', (err) => {
/* returning false here prevents Cypress from failing the test */
if (err.message.includes('ResizeObserver loop limit exceeded')) {
return false;
}
/* returning false here prevents Cypress from failing the test */
if (err.message.includes('ResizeObserver loop limit exceeded')) {
return false;
}
});
+5 -5
View File
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"allowJs": true,
"types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads"]
},
"include": ["**/*.ts"]
"compilerOptions": {
"allowJs": true,
"types": ["cypress", "cypress-localstorage-commands", "node"]
},
"include": ["**/*.ts"]
}
+2 -2
View File
@@ -3,9 +3,9 @@ services:
mermaid:
build:
context: .
dockerfile: Dockerfile.dev
target: mermaid-dev
volumes:
- ./src:/app/src
ports:
- 8080:8080
- 3000:3000
- 24678:24678
+3
View File
@@ -0,0 +1,3 @@
[build.environment]
MERMAID_ANALYTICS_URL = 'https://p.mermaid.live'
MERMAID_DOMAIN = 'mermaid.live'
+1 -1
View File
@@ -1,5 +1,5 @@
server {
listen 80;
listen 8080;
server_name mermaid;
location / {
root /usr/share/nginx/html;
+98 -92
View File
@@ -1,94 +1,100 @@
{
"name": "mermaid-live-editor",
"version": "2.0.67",
"type": "module",
"license": "MIT",
"scripts": {
"dev": "vite dev",
"dev:force": "MERMAID_LOCAL=true yarn dev --force",
"dev:test": "yarn dev",
"build": "vite build",
"preview": "vite preview",
"lint": "prettier --check --cache --plugin-search-dir=. .;eslint --ignore-path .gitignore .",
"lint:fix": "prettier --write --cache --plugin-search-dir=. .;eslint --fix --ignore-path .gitignore .",
"format": "prettier --write --cache --plugin-search-dir=. .",
"pre-commit": "lint-staged",
"postinstall": "husky install; svelte-kit sync",
"test:unit": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:browser": "cypress run",
"test": "test:unit && test:browser",
"cy": "cypress open"
},
"devDependencies": {
"@cypress/snapshot": "2.1.7",
"@sveltejs/adapter-static": "1.0.0-next.44",
"@sveltejs/kit": "1.0.0-next.511",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/svelte": "3.2.2",
"@types/pako": "2.0.0",
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.39.0",
"@typescript-eslint/parser": "5.39.0",
"@vitest/ui": "0.24.0",
"autoprefixer": "10.4.12",
"c8": "7.12.0",
"chai": "4.3.6",
"cssnano": "5.1.13",
"cy-verify-downloads": "0.1.11",
"cypress": "10.9.0",
"cypress-localstorage-commands": "2.2.1",
"eslint": "8.25.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-cypress": "2.12.1",
"eslint-plugin-es": "4.1.0",
"eslint-plugin-postcss-modules": "2.0.0",
"eslint-plugin-svelte3": "4.0.0",
"eslint-plugin-tailwindcss": "3.6.2",
"eslint-plugin-vitest": "0.0.8",
"esserializer": "1.3.2",
"husky": "8.0.1",
"jsdom": "20.0.1",
"lint-staged": "13.0.3",
"node-html-parser": "6.1.1",
"postcss": "8.4.17",
"postcss-load-config": "4.0.1",
"prettier": "2.7.1",
"prettier-plugin-svelte": "2.8.0",
"svelte": "3.50.1",
"svelte-preprocess": "4.10.7",
"tailwindcss": "3.1.8",
"tslib": "2.4.0",
"typescript": "4.8.4",
"vite": "3.1.7",
"vitest": "0.24.0"
},
"dependencies": {
"analytics": "0.8.1",
"analytics-plugin-plausible": "0.0.6",
"daisyui": "2.31.0",
"js-base64": "3.7.2",
"mermaid": "9.2.0-rc6",
"moment": "2.29.4",
"monaco-editor": "0.34.0",
"monaco-mermaid": "1.0.6",
"pako": "2.0.4",
"random-word-slugs": "0.1.6",
"svg-pan-zoom": "3.6.1",
"uuid": "9.0.0"
},
"lint-staged": {
"*.{ts,svelte,js,css,md,json}": [
"prettier --plugin-search-dir=. --write",
"eslint --ignore-path .gitignore "
]
},
"volta": {
"node": "18.5.0",
"yarn": "1.22.19"
},
"engines": {
"node": ">=16.7"
}
"name": "mermaid-live-editor",
"version": "2.0.67",
"type": "module",
"license": "MIT",
"scripts": {
"dev": "vite dev",
"dev:force": "MERMAID_LOCAL=true yarn dev --force",
"dev:test": "yarn dev",
"build": "vite build",
"preview": "vite preview",
"lint": "prettier --check --cache . && eslint --ignore-path .gitignore .",
"lint:fix": "prettier --write --cache . && eslint --fix --ignore-path .gitignore .",
"format": "prettier --write --cache .",
"pre-commit": "lint-staged",
"postinstall": "husky install && svelte-kit sync && (git config blame.ignoreRevsFile .git-blame-ignore-revs || true)",
"test:unit": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:browser": "cypress run",
"test": "test:unit && test:browser",
"cy": "cypress open"
},
"devDependencies": {
"@cypress/snapshot": "2.1.7",
"@fortawesome/fontawesome-free": "^6.5.1",
"@sveltejs/adapter-static": "3.0.2",
"@sveltejs/kit": "2.5.16",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@testing-library/svelte": "4.2.3",
"@types/lodash-es": "^4.17.12",
"@types/pako": "2.0.3",
"@types/uuid": "9.0.8",
"@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"@vitest/ui": "^1.1.3",
"autoprefixer": "^10.4.14",
"c8": "7.14.0",
"chai": "^4.3.7",
"cssnano": "^6.0.0",
"cypress": "12.17.4",
"cypress-localstorage-commands": "2.2.6",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-cypress": "2.15.2",
"eslint-plugin-es": "^4.1.0",
"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-postcss-modules": "^2.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-plugin-tailwindcss": "^3.13.1",
"eslint-plugin-unicorn": "^50.0.1",
"eslint-plugin-vitest": "^0.5.0",
"esserializer": "^1.3.11",
"husky": "^8.0.3",
"jsdom": "^21.1.2",
"lint-staged": "^15.2.0",
"node-html-parser": "^6.1.5",
"postcss": "^8.4.33",
"postcss-load-config": "5.1.0",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.6.0",
"svelte": "^4.2.8",
"svelte-preprocess": "^5.1.3",
"tailwindcss": "^3.4.1",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vitest": "^1.1.3",
"vitest-dom": "^0.1.1"
},
"dependencies": {
"@mermaid-js/mermaid-zenuml": "^0.2.0",
"daisyui": "2.52.0",
"dayjs": "^1.11.7",
"js-base64": "3.7.7",
"lodash-es": "^4.17.21",
"mermaid": "10.9.1",
"monaco-editor": "0.49.0",
"pako": "2.1.0",
"plausible-tracker": "^0.3.8",
"random-word-slugs": "0.1.7",
"svg-pan-zoom": "3.6.1",
"svg2roughjs": "^3.2.0",
"uuid": "9.0.1"
},
"lint-staged": {
"*.{ts,svelte,js,css,md,json}": [
"prettier --plugin-search-dir=. --write",
"eslint --ignore-path .gitignore "
]
},
"volta": {
"node": "18.20.3",
"yarn": "1.22.22"
},
"engines": {
"node": ">=16.7"
}
}
+10 -10
View File
@@ -6,18 +6,18 @@ const mode = process.env.NODE_ENV;
const dev = mode === 'development';
module.exports = {
plugins: [
// Some plugins, like postcss-nested, need to run before Tailwind
plugins: [
// Some plugins, like postcss-nested, need to run before Tailwind
tailwindcss,
tailwindcss,
// But others, like autoprefixer, need to run after
// But others, like autoprefixer, need to run after
autoprefixer,
autoprefixer,
!dev &&
cssnano({
preset: 'default'
})
]
!dev &&
cssnano({
preset: 'default'
})
]
};
+26 -22
View File
@@ -1,24 +1,28 @@
{
"extends": [
"config:base",
":rebaseStalePrs",
"group:allNonMajor",
"schedule:earlyMondays",
":automergeMinor",
":automergeTesters",
":automergeLinters",
":automergeTypes",
":automergePatch"
],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
}
],
"dependencyDashboard": true,
"major": {
"dependencyDashboardApproval": true
},
"dependencyDashboardAutoclose": true
"extends": [
"config:base",
":rebaseStalePrs",
"group:allNonMajor",
"schedule:earlyMondays",
":automergeMinor",
":automergeTesters",
":automergeLinters",
":automergeTypes",
":automergePatch"
],
"packageRules": [
{
"matchPackagePatterns": ["^mermaid"],
"groupName": "Mermaid packages"
},
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
}
],
"dependencyDashboard": true,
"major": {
"dependencyDashboardApproval": true
},
"dependencyDashboardAutoclose": true
}
+21 -55
View File
@@ -1,57 +1,23 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Online FlowChart &amp; Diagrams Editor - Mermaid Live Editor</title>
<meta
name="og:image"
content="https://github.com/mermaid-js/mermaid/raw/develop/img/header.png" />
<link rel="canonical" href="https://mermaid.live" />
<meta
name="description"
content="Simplify documentation and avoid heavy tools. Open source Visio Alternative. Commonly used for explaining your code! Mermaid is a simple markdown-like script language for generating charts from text via javascript." />
<link rel="icon" type="image/png" href="%sveltekit.assets%/favicon.svg" />
<link rel="mask-icon" href="%sveltekit.assets%/favicon.svg" color="#000000" />
<meta name="theme-color" content="#6366F1" />
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.min.css"
integrity="sha512-iQEIc0rsSDujsfjtD+lfyJ1W23Bh/lbgriubKDAym6VlEIDRj9rrbSIyJRyshOrl8s0yRcQ0+gyrZfSLyjJGWQ=="
crossorigin="anonymous"
referrerpolicy="no-referrer" />
<script>
var require = {
paths: {
vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs'
}
};
</script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.min.js"
integrity="sha512-6bIYsGqvLpAiEBXPdRQeFf5cueeBECtAKJjIHer3BhBZNTV3WLcLA8Tm3pDfxUwTMIS+kAZwTUvJ1IrMdX8C5w=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.nls.min.js"
integrity="sha512-CCv+DKWw+yZhxf4Z+ExT6HC5G+3S45TeMTYcJyYbdrv4BpK2vyALJ4FoVR/KGWDIPu7w4tNCOC9MJQIkYPR5FA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/editor/editor.main.js"
integrity="sha512-TTPQbVI87mnVMV+1KbkKJ8vdQ4QqqbKyuTtJ9wQD8CqnwQLSQgXH7MWOQ88VO7pRzxWhqI1vYDeEV651sAH4ig=="
crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
%sveltekit.head%
</head>
<body>
<div id="svelte">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Online FlowChart &amp; Diagrams Editor - Mermaid Live Editor</title>
<meta
name="og:image"
content="https://github.com/mermaid-js/mermaid/raw/develop/img/header.png" />
<link rel="canonical" href="https://mermaid.live" />
<meta
name="description"
content="Simplify documentation and avoid heavy tools. Open source Visio Alternative. Commonly used for explaining your code! Mermaid is a simple markdown-like script language for generating charts from text via javascript." />
<link rel="icon" type="image/png" href="%sveltekit.assets%/favicon.svg" />
<link rel="mask-icon" href="%sveltekit.assets%/favicon.svg" color="#000000" />
<meta name="theme-color" content="#6366F1" />
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
%sveltekit.head%
</head>
<body style="overflow: hidden">
<div id="svelte">%sveltekit.body%</div>
</body>
</html>
+9 -2
View File
@@ -1,10 +1,17 @@
@import '@fortawesome/fontawesome-free/css/all.min.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
.input {
@apply flex-1 border-primary border-solid border-2 rounded;
@apply flex-1 rounded border-2 border-solid border-primary;
}
.action-btn {
@apply btn btn-primary;
@apply btn btn-primary;
}
#graph-div sub,
#graph-div sup {
position: initial;
vertical-align: revert;
}
+6 -7
View File
@@ -1,14 +1,13 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly MERMAID_RENDERER_URL: string;
readonly MERMAID_KROKI_RENDERER_URL: string;
readonly MERMAID_CDN_URL: string;
readonly MERMAID_BASE_URL: string;
readonly MERMAID_LOCAL: boolean;
// more env variables...
readonly MERMAID_RENDERER_URL?: string;
readonly MERMAID_KROKI_RENDERER_URL?: string;
readonly MERMAID_ANALYTICS_URL?: string;
readonly MERMAID_DOMAIN?: string;
// more env variables...
}
interface ImportMeta {
readonly env: ImportMetaEnv;
readonly env: ImportMetaEnv;
}
-14
View File
@@ -1,15 +1 @@
/* eslint-disable @typescript-eslint/no-empty-interface */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-unused-vars */
/// <reference types="@sveltejs/kit" />
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
declare global {
namespace jest {
interface Matchers<R = void>
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
extends TestingLibraryMatchers<typeof expect.stringContaining, R> {}
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event, {});
return response;
const response = await resolve(event, {});
return response;
};
+269
View File
@@ -0,0 +1,269 @@
<script lang="ts">
import { browser } from '$app/environment';
import Card from '$lib/components/Card/Card.svelte';
import { waitForRender } from '$lib/util/autoSync';
import { env } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import dayjs from 'dayjs';
import { toBase64 } from 'js-base64';
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';
const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}/css/all.min.css`;
const { krokiRendererUrl, rendererUrl } = env;
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
const getFileName = (extension: string) =>
`mermaid-diagram-${dayjs().format('YYYY-MM-DD-HHmmss')}.${extension}`;
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
if (svg) {
// Prevents the SVG size of the interface from being changed
svg = svg.cloneNode(true) as HTMLElement;
}
height && svg?.setAttribute('height', `${height}px`);
width && svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
if (!svg) {
svg = getSvgElement();
}
const svgString = svg.outerHTML
.replaceAll('<br>', '<br/>')
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`);
return toBase64(`<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="${FONT_AWESOME_URL}" type="text/css"?>
${svgString}`);
};
const exportImage = async (event: Event, exporter: Exporter) => {
await waitForRender();
if (document.querySelector('.outOfSync')) {
throw new Error('Diagram is out of sync');
}
const canvas: HTMLCanvasElement = document.createElement('canvas');
const svg = document.querySelector<HTMLElement>('#container svg');
if (!svg) {
throw new Error('svg not found');
}
const box: DOMRect = svg.getBoundingClientRect();
canvas.width = box.width;
canvas.height = box.height;
if (imagemodeselected === 'width') {
const ratio = box.height / box.width;
canvas.width = userimagesize;
canvas.height = userimagesize * ratio;
} else if (imagemodeselected === 'height') {
const ratio = box.width / box.height;
canvas.width = userimagesize * ratio;
canvas.height = userimagesize;
}
const context = canvas.getContext('2d');
if (!context) {
throw new Error('context not found');
}
context.fillStyle = `hsl(${window.getComputedStyle(document.body).getPropertyValue('--b1')})`;
context.fillRect(0, 0, canvas.width, canvas.height);
const image = new Image();
image.addEventListener('load', exporter(context, image));
image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`;
event.stopPropagation();
event.preventDefault();
};
const getSvgElement = () => {
const svgElement = document.querySelector('#container svg')?.cloneNode(true) as HTMLElement;
svgElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
return svgElement;
};
const simulateDownload = (download: string, href: string): void => {
const a = document.createElement('a');
a.download = download;
a.href = href;
a.click();
a.remove();
};
const downloadImage: Exporter = (context, image) => {
return () => {
const { canvas } = context;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
simulateDownload(
getFileName('png'),
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
);
};
};
const isClipboardAvailable = (): boolean => {
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem') as boolean;
};
const clipboardCopy: Exporter = (context, image) => {
return () => {
const { canvas } = context;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
try {
if (!blob) {
throw new Error('blob is empty');
}
void navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob
})
]);
} catch (error) {
console.error(error);
}
});
};
};
const onCopyClipboard = async (event: Event) => {
await exportImage(event, clipboardCopy);
logEvent('copyClipboard');
};
const onDownloadPNG = async (event: Event) => {
await exportImage(event, downloadImage);
logEvent('download', {
type: 'png'
});
};
const onDownloadSVG = () => {
simulateDownload(getFileName('svg'), `data:image/svg+xml;base64,${getBase64SVG()}`);
logEvent('download', {
type: 'svg'
});
};
const onCopyMarkdown = () => {
document.querySelector<HTMLInputElement>('#markdown')?.select();
document.execCommand('Copy');
logEvent('copyMarkdown');
};
let gistURL = '';
stateStore.subscribe(({ loader }) => {
if (loader?.type === 'gist') {
// @ts-expect-error Gist will have url
gistURL = loader.config.url;
}
});
const loadGist = () => {
if (!gistURL) {
alert('Please enter a Gist URL first');
}
window.location.href = `${window.location.pathname}?gist=${gistURL}`;
logEvent('loadGist');
};
let iUrl: string;
let svgUrl: string;
let krokiUrl: string;
let mdCode: string;
let imagemodeselected = 'auto';
let userimagesize = 1080;
let isNetlify = false;
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
stateStore.subscribe(({ code, serialized }) => {
iUrl = `${rendererUrl}/img/${serialized}?type=png`;
svgUrl = `${rendererUrl}/svg/${serialized}`;
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#${serialized})`;
});
</script>
<Card title="Actions" isOpen={false}>
<div class="m-2 flex flex-wrap gap-2">
{#if isClipboardAvailable()}
<button class="action-btn w-full" on:click={onCopyClipboard}
><i class="far fa-copy mr-2" /> Copy Image to clipboard
</button>
{/if}
<button id="downloadPNG" class="action-btn flex-grow" on:click={onDownloadPNG}>
<i class="fas fa-download mr-2" /> PNG
</button>
<button id="downloadSVG" class="action-btn flex-grow" on:click={onDownloadSVG}>
<i class="fas fa-download mr-2" /> SVG
</button>
<a target="_blank" rel="noreferrer" class="flex-grow" href={iUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> PNG
</button>
</a>
<a target="_blank" rel="noreferrer" class="flex-grow" href={svgUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> SVG
</button>
</a>
<a target="_blank" rel="noreferrer" class="flex-grow" href={krokiUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> Kroki
</button>
</a>
<div class="flex items-center gap-2">
PNG size
<label for="autosize">
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} /> Auto
</label>
<label for="width">
<input type="radio" value="width" id="width" bind:group={imagemodeselected} /> Width
</label>
<label for="height">
<input type="radio" value="height" id="height" bind:group={imagemodeselected} /> Height
</label>
{#if imagemodeselected !== 'auto'}
<input
id="height"
class="input"
type="number"
min="3"
max="10000"
bind:value={userimagesize} />
{/if}
</div>
<div class="flex w-full items-center gap-2">
<input class="input" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
<label for="markdown">
<button class="btn btn-primary btn-md flex-auto" on:click={onCopyMarkdown}>
Copy Markdown
</button>
</label>
</div>
<div class="flex w-full items-center gap-2">
<input
class="input"
id="gist"
type="text"
bind:value={gistURL}
placeholder="Enter Gist URL" />
<label for="gist">
<button class="btn btn-primary btn-md flex-auto" on:click={loadGist}> Load Gist </button>
</label>
</div>
{#if isNetlify}
<div class="flex w-full items-center justify-center">
<a class="link text-sm text-gray-500 underline" href="https://netlify.com">
This site is powered by Netlify
</a>
</div>
{/if}
</div>
</Card>
+33
View File
@@ -0,0 +1,33 @@
<script lang="ts">
import type { Tab } from '$lib/types';
import { slide } from 'svelte/transition';
import Tabs from './Tabs.svelte';
export let isCloseable = true;
export let isOpen = true;
export let tabs: Tab[] = [];
export let activeTabID = '';
export let title: string;
$: isOpen = isCloseable ? isOpen : true;
$: isTabsShown = isOpen && tabs.length > 0;
</script>
<div class="card m-2 flex flex-grow flex-col overflow-hidden rounded shadow-2xl">
<div
role="toolbar"
tabindex="0"
class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer"
on:click={() => (isOpen = !isOpen)}
on:keypress={() => (isOpen = !isOpen)}>
<div class="flex justify-between">
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} {activeTabID} />
<div class="flex items-center gap-x-4 {isTabsShown ? '-mt-2' : ''}">
<slot name="actions" />
</div>
</div>
</div>
{#if isOpen}
<div class="card-body flex-grow overflow-auto p-0 text-base-content" transition:slide>
<slot />
</div>
{/if}
</div>
+56
View File
@@ -0,0 +1,56 @@
<script lang="ts">
import type { Tab, TabEvents } from '$lib/types';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
export let isCloseable = true;
export let tabs: Tab[];
export let title: string;
export let isOpen = false;
export let activeTabID: string;
if (!activeTabID && tabs.length > 0) {
activeTabID = tabs[0].id;
}
const dispatch = createEventDispatcher<TabEvents>();
const toggleTabs = (tab: Tab) => {
activeTabID = tab.id;
dispatch('select', tab);
};
</script>
<div class="flex cursor-default">
<span
role="menubar"
tabindex="0"
class="mr-2 font-semibold"
on:click|stopPropagation={() => (isOpen = !isOpen)}
on:keypress|stopPropagation={() => (isOpen = !isOpen)}>
{#if isCloseable}
<i class="fas fa-chevron-right icon" class:isOpen />
{/if}
{title}</span>
{#if isOpen && tabs}
<ul class="tabs" transition:fade>
{#each tabs as tab}
<div
role="tab"
tabindex="0"
class="tab tab-lifted {activeTabID === tab.id ? 'tab-active' : 'text-primary-content'}"
on:click|stopPropagation={() => toggleTabs(tab)}
on:keypress|stopPropagation={() => toggleTabs(tab)}>
<i class="mr-1 {tab.icon}" />
{tab.title}
</div>
{/each}
</ul>
{/if}
</div>
<style>
.icon {
transition-duration: 0.5s;
}
.isOpen {
transform: rotate(90deg);
}
</style>
+23
View File
@@ -0,0 +1,23 @@
import { cleanup, render } from '@testing-library/svelte';
import { describe, expect, it, afterEach } from 'vitest';
import Card from './Card.svelte';
describe('card.svelte', () => {
// TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
afterEach(() => cleanup());
it('mounts', () => {
const { container } = render(Card, {
title: 'TabTest',
tabs: [
{ id: 't1', title: 'title1', icon: 'fab fa-git-alt' },
{ id: 't2', title: 'title2', icon: 'far fa-bookmark' }
]
});
expect(container).toBeTruthy();
expect(container).toHaveTextContent('TabTest');
expect(container).toHaveTextContent('title1');
expect(container).toHaveTextContent('title2');
expect(container).not.toHaveTextContent('title3');
});
});
+133
View File
@@ -0,0 +1,133 @@
<script lang="ts" context="module">
declare global {
interface Window {
Cypress: boolean;
editorLoaded: boolean;
}
}
</script>
<script lang="ts">
import type { EditorMode } from '$lib/types';
import { stateStore, updateCode, updateConfig } from '$lib/util/state';
import { themeStore } from '$lib/util/theme';
import { errorDebug, syncDiagram } from '$lib/util/util';
import * as monaco from 'monaco-editor';
import monacoJsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import monacoEditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import { onMount } from 'svelte';
import { initEditor } from '$lib/util/monacoExtra';
import { logEvent } from '$lib/util/stats';
let divElement: HTMLDivElement | undefined;
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
minimap: {
enabled: false
},
theme: 'mermaid',
overviewRulerLanes: 0
};
let text = '';
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
// console.log('editor store subscription', { code, mermaid });
if (!editor) {
return;
}
// Update editor text if it's different
const newText = editorMode === 'code' ? code : mermaid;
if (newText !== text) {
// console.log('updating editor text', newText);
editor.setScrollTop(0);
editor.setValue(newText);
text = newText;
}
// Update editor mode if it's different
const language = editorMode === 'code' ? 'mermaid' : 'json';
const model = editor.getModel();
if (!model) {
console.error("editor model doesn't exist");
return;
}
if (model.getLanguageId() !== language) {
monaco.editor.setModelLanguage(model, language);
}
// Display/clear errors
monaco.editor.setModelMarkers(model, 'mermaid', errorMarkers);
});
themeStore.subscribe(({ isDark }) => {
editor && monaco.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
});
const handleUpdate = (text: string, mode: EditorMode) => {
// console.log('editor HandleUpdate', { text, mode });
if (mode === 'code') {
updateCode(text);
} else {
updateConfig(text);
}
};
onMount(() => {
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new monacoJsonWorker();
}
return new monacoEditorWorker();
}
};
if (!divElement) {
throw new Error('divEl is undefined');
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
initEditor(monaco);
errorDebug();
editor = monaco.editor.create(divElement, editorOptions);
editor.onDidChangeModelContent(({ isFlush }) => {
const newText = editor?.getValue();
if (!newText || text === newText || isFlush) {
return;
}
text = newText;
handleUpdate(text, $stateStore.editorMode);
});
editor.addAction({
id: 'mermaid-render-diagram',
label: 'Render Diagram',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
run: function () {
syncDiagram();
logEvent('renderDiagram', {
method: 'keyboardShortcut'
});
}
});
monaco.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor?.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
if (divElement.parentElement) {
resizeObserver.observe(divElement.parentElement);
}
if (window.Cypress) {
window.editorLoaded = true;
}
return () => {
editor?.dispose();
};
});
</script>
<div bind:this={divElement} id="editor" class="overflow-hidden" />
+190
View File
@@ -0,0 +1,190 @@
<script lang="ts">
import Card from '$lib/components/Card/Card.svelte';
import { inputStateStore, getStateString } from '$lib/util/state';
import {
addHistoryEntry,
historyModeStore,
clearHistoryData,
getPreviousState,
historyStore,
loaderHistoryStore,
restoreHistory
} from './history';
import { notify, prompt } from '$lib/util/notify';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import dayjs from 'dayjs';
import dayjsRelativeTime from 'dayjs/plugin/relativeTime';
import type { HistoryEntry, HistoryType, State, Tab } from '$lib/types';
import { logEvent } from '$lib/util/stats';
dayjs.extend(dayjsRelativeTime);
const HISTORY_SAVE_INTERVAL = 60_000;
const tabSelectHandler = (message: CustomEvent<Tab>) => {
historyModeStore.set(message.detail.id as HistoryType);
};
let tabs: Tab[] = [
{
id: 'manual',
title: 'Saved',
icon: 'far fa-bookmark'
},
{
id: 'auto',
title: 'Timeline',
icon: 'fas fa-history'
}
];
const downloadHistory = () => {
const data = get(historyStore);
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `mermaid-history-${dayjs().format('YYYY-MM-DD-HHmmss')}.json`;
a.click();
URL.revokeObjectURL(url);
logEvent('history', {
action: 'download'
});
};
const uploadHistory = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';
input.addEventListener('change', async ({ target }: Event) => {
const file = (target as HTMLInputElement)?.files?.[0];
if (!file) {
return;
}
const data: HistoryEntry[] = JSON.parse(await file.text());
restoreHistory(data);
});
input.click();
};
const saveHistory = (auto = false) => {
const currentState: string = getStateString();
const previousState: string = getPreviousState(auto);
if (previousState !== currentState) {
addHistoryEntry({
state: $inputStateStore,
time: Date.now(),
type: auto ? 'auto' : 'manual'
});
} else if (!auto) {
notify('State already saved.');
}
};
const clearHistory = (id?: string): void => {
if (!id && !prompt('Clear all saved items?')) {
return;
}
clearHistoryData(id);
};
const restoreHistoryItem = (state: State): void => {
inputStateStore.set({ ...state, updateDiagram: true });
};
const relativeTime = (time: number) => {
const t = new Date(time);
return `${new Date(t).toLocaleString()} (${dayjs(t).fromNow()})`;
};
onMount(() => {
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 = false;
</script>
<Card on:select={tabSelectHandler} bind:isOpen {tabs} title="History">
<div slot="actions">
<button
id="uploadHistory"
class="btn btn-secondary btn-xs w-12"
on:click|stopPropagation={() => uploadHistory()}
title="Upload history"><i class="fa fa-upload" /></button>
{#if $historyStore.length > 0}
<button
id="downloadHistory"
class="btn btn-secondary btn-xs w-12"
on:click|stopPropagation={() => downloadHistory()}
title="Download history"><i class="fa fa-download" /></button>
{/if}
|
<button
id="saveHistory"
class="btn btn-success btn-xs w-12"
on:click|stopPropagation={() => saveHistory()}
title="Save current state"><i class="far fa-save" /></button>
{#if $historyModeStore !== 'loader'}
<button
id="clearHistory"
class="btn btn-error btn-xs w-12"
on:click|stopPropagation={() => clearHistory()}
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
{/if}
</div>
<ul class="h-56 space-y-2 overflow-auto p-2" id="historyList">
{#if $historyStore.length > 0}
{#each $historyStore as { id, state, time, name, url, type }}
<li class="flex-col rounded p-2 shadow">
<div class="flex">
<div class="flex-1">
<div class="flex flex-col text-base-content">
{#if url}
<a
href={url}
target="_blank"
title="Open revision in new tab"
class="text-blue-500 hover:underline">{name}</a>
{:else}
<span>{name}</span>
{/if}
<span class="text-sm text-gray-400">{relativeTime(time)}</span>
</div>
</div>
<div class="flex content-center gap-2">
<button class="btn btn-success" on:click={() => restoreHistoryItem(state)}
><i class="fas fa-undo mr-1" />Restore</button>
{#if type !== 'loader'}
<button class="btn btn-error" on:click={() => clearHistory(id)}
><i class="fas fa-trash-alt mr-1" />Delete</button>
{/if}
</div>
</div>
</li>
{/each}
{:else}
<div class="m-2">
No items in History<br />
Click the Save button to save current state and restore it later.<br />
Timeline will automatically be saved every minute.
</div>
{/if}
</ul>
</Card>
+127
View File
@@ -0,0 +1,127 @@
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import type { HistoryEntry } from '$lib/types';
import { describe, it, expect } from 'vitest';
import {
addHistoryEntry,
injectHistoryIDs,
clearHistoryData,
historyModeStore,
historyStore
} from './history';
import { defaultState } from '$lib/util/state';
import { get } from 'svelte/store';
describe('history', () => {
it('should handle saving individual history entry', () => {
expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
addHistoryEntry({
state: defaultState,
time: 12_345,
type: 'manual'
});
const [manualEntry] = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualEntry.time).toBe(12_345);
expect(manualEntry.type).toBe('manual');
expect(manualEntry.name).not.toBeNull();
expect(manualEntry.state).not.toBeNull();
addHistoryEntry({
state: defaultState,
time: 54_321,
type: 'auto'
});
const [autoEntry] = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(autoEntry.time).toBe(54_321);
expect(autoEntry.type).toBe('auto');
expect(autoEntry.name).not.toBeNull();
expect(autoEntry.state).not.toBeNull();
historyModeStore.set('manual');
clearHistoryData();
historyModeStore.set('auto');
clearHistoryData();
expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
});
it('should clear history entries', () => {
addHistoryEntry({
state: defaultState,
time: 12_345,
type: 'manual'
});
addHistoryEntry({
state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
time: 123_456,
type: 'manual'
});
historyModeStore.set('manual');
const store: HistoryEntry[] = get(historyStore);
expect(store.length).toBe(2);
clearHistoryData(store[1].id);
expect(get(historyStore).length).toBe(1);
clearHistoryData();
expect(get(historyStore).length).toBe(0);
historyModeStore.set('auto');
addHistoryEntry({
state: defaultState,
time: 54_321,
type: 'auto'
});
addHistoryEntry({
state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
time: 654_321,
type: 'auto'
});
expect(get(historyStore).length).toBe(2);
clearHistoryData();
expect(get(historyStore).length).toBe(0);
// Test calling when history is empty
clearHistoryData();
expect(get(historyStore).length).toBe(0);
});
});
describe('history migration', () => {
it('should inject history IDs as migration', () => {
window.localStorage.setItem(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
);
window.localStorage.setItem(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
let manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
injectHistoryIDs();
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
});
+152
View File
@@ -0,0 +1,152 @@
import { derived, writable, get } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store';
import { persist, localStorage } from '$lib/util/persist';
import { generateSlug } from 'random-word-slugs';
import type { HistoryEntry, HistoryType, Optional } from '$lib/types';
import { v4 as uuidV4 } from 'uuid';
import { logEvent } from '$lib/util/stats';
const MAX_AUTO_HISTORY_LENGTH = 30;
export const historyModeStore: Writable<HistoryType> = persist(
writable('manual'),
localStorage(),
'autoHistoryMode'
);
const autoHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
'autoHistoryStore'
);
const manualHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
'manualHistoryStore'
);
export const loaderHistoryStore: Writable<HistoryEntry[]> = writable([]);
export const historyStore: Readable<HistoryEntry[]> = derived(
[historyModeStore, autoHistoryStore, manualHistoryStore, loaderHistoryStore],
([historyMode, autoHistories, manualHistories, loadedHistories], set) => {
switch (historyMode) {
case 'auto': {
set(autoHistories);
break;
}
case 'manual': {
set(manualHistories);
break;
}
case 'loader': {
set(loadedHistories);
break;
}
default: {
set(autoHistories);
}
}
}
);
export const addHistoryEntry = (entryToAdd: Optional<HistoryEntry, 'id'>): void => {
const entry: HistoryEntry = {
...entryToAdd,
id: uuidV4()
};
if (entry.type === 'loader') {
loaderHistoryStore.update((entries) => [entry, ...entries]);
return;
}
if (!entry.name) {
entry.name = generateSlug(2);
}
if (entry.type === 'auto') {
autoHistoryStore.update((entries) => {
if (entries.length >= MAX_AUTO_HISTORY_LENGTH) {
entries = entries.slice(0, MAX_AUTO_HISTORY_LENGTH - 1);
}
return [entry, ...entries];
});
}
manualHistoryStore.update((entries) => [entry, ...entries]);
logEvent('history', { action: 'save' });
};
export const clearHistoryData = (idToClear?: string): void => {
(get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
if (get(historyModeStore) !== 'loader') {
entries = entries.filter(({ id }) => idToClear && id != idToClear);
logEvent('history', { action: 'clear', type: idToClear ? 'single' : 'all' });
}
return entries;
});
};
export const getPreviousState = (auto: boolean): string => {
const entries = get(auto ? autoHistoryStore : manualHistoryStore);
if (entries.length > 0) {
return JSON.stringify(entries[0].state);
}
return '';
};
export const restoreHistory = (data: HistoryEntry[]) => {
const entries = data.filter((element) => validateEntry(element));
const invalidEntryCount = data.length - entries.length;
if (invalidEntryCount > 0) {
console.error(`${invalidEntryCount} invalid history entries were removed.`);
console.error(data);
}
if (entries.length > 0) {
let entryCount = 0;
(entries[0].type === 'auto' ? autoHistoryStore : manualHistoryStore).update((existing) => {
const existingIDs = new Set(existing.map(({ id }) => id));
const newEntries = entries.filter(({ id }) => !existingIDs.has(id));
entryCount = newEntries.length;
const combined = [...existing, ...newEntries];
combined.sort((a, b) => b.time - a.time);
return combined;
});
alert(
`${entryCount} entries restored. ${invalidEntryCount} invalid, ${
entries.length - entryCount
} duplicates.`
);
logEvent('history', {
action: 'restore',
success: entryCount,
invalid: invalidEntryCount,
duplicates: entries.length - entryCount
});
} else {
alert('No valid entries found.');
}
};
const setIDs = (entries: HistoryEntry[]) => {
for (const entry of entries) {
if (!entry.id) {
entry.id = uuidV4();
}
}
return entries;
};
export const injectHistoryIDs = (): void => {
autoHistoryStore.update(setIDs);
manualHistoryStore.update(setIDs);
};
const validateEntry = (entry: HistoryEntry): boolean => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
return entry.type && entry.state && entry.time && true;
};
+173
View File
@@ -0,0 +1,173 @@
<script context="module" lang="ts">
import { version } from 'mermaid/package.json';
import { logEvent, plausible } from '$lib/util/stats';
void logEvent('version', {
mermaidVersion: version
});
</script>
<script lang="ts">
import Theme from './Theme.svelte';
import { dismissPromotion, getActivePromotion } from '$lib/util/promos/promo';
import Privacy from './Privacy.svelte';
let isMenuOpen = false;
function toggleMenu() {
isMenuOpen = !isMenuOpen;
}
interface Link {
href: string;
title?: string;
icon?: string;
img?: string;
}
const links: Link[] = [
{
title: 'Documentation',
href: 'https://mermaid.js.org/intro/getting-started.html'
},
{
title: 'Tutorial',
href: 'https://mermaid.js.org/ecosystem/tutorials.html'
},
{
title: 'Mermaid',
href: 'https://github.com/mermaid-js/mermaid'
},
{
title: 'CLI',
href: 'https://github.com/mermaid-js/mermaid-cli'
},
{
href: 'https://github.com/mermaid-js/mermaid-live-editor',
icon: 'fab fa-github fa-lg'
},
{
href: 'https://mermaidchart.com',
img: './mermaidchart-logo.svg'
}
];
let activePromotion = getActivePromotion();
const trackBannerClick = () => {
if (!plausible || !activePromotion) {
return;
}
logEvent('bannerClick', {
promotion: activePromotion.id
});
};
</script>
{#if activePromotion}
<div
class="top-bar z-10 flex h-fit w-full items-center justify-center bg-gradient-to-r from-[#bd34fe] to-[#ff3670] p-1 text-center text-white">
<div
class="flex flex-grow"
role="button"
tabindex="0"
on:click={trackBannerClick}
on:keypress={trackBannerClick}>
<svelte:component this={activePromotion.component} />
</div>
<button
title="Dismiss banner"
on:click={() => {
dismissPromotion(activePromotion?.id);
activePromotion = undefined;
}}>
<i class="fa fa-close px-2" />
</button>
</div>
{/if}
<div class="navbar bg-primary p-0 shadow-lg">
<div class="mx-2 flex-1 px-2">
<span class="text-lg font-bold">
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
</span>
</div>
<label
for="menu-toggle"
class={isMenuOpen ? 'hidden' : 'pointer-cursor fixed right-4 z-[1000] lg:hidden'}>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="fill-current"
width="20"
height="20"
viewBox="0 0 20 20">
<title>Menu</title>
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
</svg>
</label>
<!-- Cross SVG -->
<label
for="menu-toggle"
class={isMenuOpen ? 'pointer-cursor fixed right-4 z-[1000] lg:hidden' : 'hidden'}>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="fill-current"
width="20"
height="20"
viewBox="0 0 20 20">
<title>Cross</title>
<line x1="5" y1="5" x2="15" y2="15" stroke="white" stroke-width="2" />
<line x1="5" y1="15" x2="15" y2="5" stroke="white" stroke-width="2" />
</svg>
</label>
<input
class="hidden"
type="checkbox"
id="menu-toggle"
bind:checked={isMenuOpen}
on:click={toggleMenu} />
<div class="hidden w-full lg:flex lg:w-auto lg:items-center" id="menu">
<Theme />
<ul class="items-center justify-between pt-4 text-base lg:flex lg:pt-0">
<li>
<Privacy />
</li>
{#each links as { title, href, icon, img }}
<li>
<a class="btn btn-ghost" target="_blank" {href}>
{#if icon}
<i class={icon} />
{:else if img}
<img src={img} alt={title} />
{/if}
{#if title}
{title}
{/if}
</a>
</li>
{/each}
</ul>
</div>
</div>
<style>
#menu-toggle:checked + #menu {
position: absolute;
top: 2.5rem;
padding: 1rem 0;
background: #661ae6;
display: flex;
}
.navbar {
z-index: 10000;
}
img {
width: 1.5rem;
height: 1.5rem;
}
</style>
+213
View File
@@ -0,0 +1,213 @@
<script lang="ts">
import { updateCode } from '$lib/util/state';
import Card from '$lib/components/Card/Card.svelte';
import { logEvent } from '$lib/util/stats';
const samples = {
Flow: `flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]`,
Sequence: `sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!`,
Class: `classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}`,
State: `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`,
Gantt: `gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d`,
Pie: `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15`,
ER: `erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"`,
'User Journey': `journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 3: Me`,
Git: `gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit`,
Mindmap: `mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectivness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid`,
QuadrantChart: `quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
y-axis Low Engagement --> High Engagement
quadrant-1 We should expand
quadrant-2 Need to promote
quadrant-3 Re-evaluate
quadrant-4 May be improved
Campaign A: [0.3, 0.6]
Campaign B: [0.45, 0.23]
Campaign C: [0.57, 0.69]
Campaign D: [0.78, 0.34]
Campaign E: [0.40, 0.34]
Campaign F: [0.35, 0.78]`,
XYChart: `
xychart-beta
title "Sales Revenue"
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
y-axis "Revenue (in $)" 4000 --> 11000
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]`,
Block: `block-beta
columns 3
doc>"Document"]:3
space down1<[" "]>(down) space
block:e:3
l["left"]
m("A wide one in the middle")
r["right"]
end
space down2<[" "]>(down) space
db[("DB")]:3
space:3
D space C
db --> D
C --> db
D --> C
style m fill:#d6d,stroke:#333,stroke-width:4px
`,
ZenUML: `zenuml
title Order Service
@Actor Client #FFEBE6
@Boundary OrderController #0747A6
@EC2 <<BFF>> OrderService #E3FCEF
group BusinessService {
@Lambda PurchaseService
@AzureFunction InvoiceService
}
@Starter(Client)
// \`POST /orders\`
OrderController.post(payload) {
OrderService.create(payload) {
order = new Order(payload)
if(order != null) {
par {
PurchaseService.createPO(order)
InvoiceService.createInvoice(order)
}
}
}
}
`
};
type SampleTypes = keyof typeof samples;
const loadSampleDiagram = (diagramType: SampleTypes): void => {
updateCode(samples[diagramType], {
updateDiagram: true,
resetPanZoom: true
});
logEvent('loadSampleDiagram', { diagramType });
};
// Adding in this array will add an icon to the preset menu
const newDiagrams: SampleTypes[] = ['QuadrantChart', 'XYChart', 'Block', 'ZenUML'];
const diagramOrder: SampleTypes[] = [
'Flow',
'Sequence',
'Class',
'State',
'ER',
'Gantt',
'User Journey',
'Git',
'Pie',
'Mindmap',
'QuadrantChart',
'XYChart',
'Block',
'ZenUML'
];
</script>
<Card title="Sample Diagrams" isOpen={false}>
<div class="flex flex-wrap gap-2 p-2">
{#each diagramOrder as sample}
<button
class="btn btn-primary btn-sm w-fit min-w-20 flex-grow normal-case"
on:click={() => loadSampleDiagram(sample)}>
{sample}
{#if newDiagrams.includes(sample)}
<span class="fa fa-heart ml-2" />
{/if}
</button>
{/each}
</div>
</Card>
+42
View File
@@ -0,0 +1,42 @@
<label for="privacyModal" class="btn btn-ghost flex gap-2">
<i class="fa-solid fa-shield-halved text-xl"></i> Security
</label>
<input type="checkbox" id="privacyModal" class="modal-toggle" />
<div class="modal" role="dialog">
<div class="modal-box flex flex-col gap-4 text-base-content">
<h1 class="text-3xl font-bold">Data security</h1>
<p>
<span class="text-xl font-extrabold">
The content of the diagrams you create never leaves your browser.
</span>
It's stored in the URL, and the browser's local storage only.
</p>
<p>
Mermaid live editor is a fully client side application, that will also work as an offline <a
href="https://web.dev/explore/progressive-web-apps"
class="link"
target="_blank">
PWA.
</a>
</p>
<p>
The only server we have is a self hosted version of the open source and privacy friendly
Plausible Analytics. We only collect data related to actions performed, like the type of
diagram rendered, number of times a feature was used, etc. <br /> All the data we collect is
anonymized and
<a href="https://p.mermaid.live/mermaid.live" class="link" target="_blank">
available publicly.
</a>
</p>
<p>
Additional services like the external PNG/SVG/Kroki links and "Save to Mermaid Chart" feature
will share your diagram with the respective 3rd party service.
</p>
<label class="btn btn-circle btn-ghost btn-sm absolute right-2 top-2" for="privacyModal">
X
</label>
</div>
</div>
+66
View File
@@ -0,0 +1,66 @@
<script lang="ts">
import { setTheme, themeStore } from '$lib/util/theme';
const themes = [
'🌝 light',
'🌚 dark',
'🧁 cupcake',
'🐝 bumblebee',
'✳️ emerald',
'🏢 corporate',
'🌃 synthwave',
'👴 retro',
'🤖 cyberpunk',
'🌸 valentine',
'🎃 halloween',
'🌷 garden',
'🌲 forest',
'🐟 aqua',
'👓 lofi',
'🖍 pastel',
'🧚‍♀️ fantasy',
'📝 wireframe',
'🏴 black',
'💎 luxury',
'🧛‍♂️ dracula'
];
</script>
<div class="dropdown hidden lg:block">
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<div tabindex="0" class="btn btn-ghost">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="inline-block h-6 w-6 stroke-current md:mr-2"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>
<span class="hidden md:inline">Theme</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1792 1792"
class="ml-1 inline-block h-4 w-4 fill-current"
><path
d="M1395 736q0 13-10 23l-466 466q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l393 393 393-393q10-10 23-10t23 10l50 50q10 10 10 23z" /></svg>
</div>
<div
class="dropdown-content top-px mt-14 h-96 w-56 overflow-y-auto bg-base-200 text-base-content shadow-2xl">
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<ul tabindex="0" class="menu compact p-4">
{#each themes as theme}
<li class:bordered={$themeStore.theme !== undefined && theme.includes($themeStore.theme)}>
<span
role="menuitem"
tabindex="0"
class="btn btn-ghost justify-start"
on:click={() => setTheme(theme)}
on:keypress={() => setTheme(theme)}>{theme}</span>
</li>
{/each}
</ul>
</div>
</div>
+210
View File
@@ -0,0 +1,210 @@
<script lang="ts">
import type { State, ValidatedState } from '$lib/types';
import { recordRenderTime, shouldRefreshView } from '$lib/util/autoSync';
import { render as renderDiagram } from '$lib/util/mermaid';
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { logEvent, saveStatistics } from '$lib/util/stats';
import { cmdKey } from '$lib/util/util';
import type { MermaidConfig } from 'mermaid';
import { onMount } from 'svelte';
import panzoom from 'svg-pan-zoom';
import { Svg2Roughjs } from 'svg2roughjs';
let code = '';
let config = '';
let container: HTMLDivElement;
let rough: boolean;
let view: HTMLDivElement;
let error = false;
let errorLines: string[] = [];
let outOfSync = false;
let hide = false;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let pzoom: typeof panzoom | undefined;
const handlePanZoomChange = () => {
if (!pzoom) {
return;
}
const pan = pzoom.getPan();
const zoom = pzoom.getZoom();
updateCodeStore({ pan, zoom });
logEvent('panZoom');
};
const handlePanZoom = (state: State) => {
if (!state.panZoom) {
return;
}
hide = true;
pzoom?.destroy();
pzoom = undefined;
void Promise.resolve().then(() => {
const graphDiv = document.querySelector<HTMLElement>('#graph-div');
if (!graphDiv) {
return;
}
pzoom = panzoom(graphDiv, {
onPan: handlePanZoomChange,
onZoom: handlePanZoomChange,
controlIconsEnabled: true,
fit: true,
center: true
});
const { pan, zoom } = state;
if (pan !== undefined && zoom !== undefined && Number.isFinite(zoom)) {
pzoom.zoom(zoom);
pzoom.pan(pan);
}
hide = false;
});
};
const handleStateChange = async (state: ValidatedState) => {
const startTime = Date.now();
if (state.error !== undefined) {
error = true;
errorLines = state.error.toString().split('\n');
return;
}
error = false;
try {
if (container && state && (state.updateDiagram || state.autoSync)) {
if (!state.autoSync) {
$inputStateStore.updateDiagram = false;
}
outOfSync = false;
manualUpdate = true;
// Do not render if there is no change in Code/Config/PanZoom
if (
code === state.code &&
config === state.mermaid &&
panZoomEnabled === state.panZoom &&
rough === state.rough
) {
return;
}
if (!shouldRefreshView()) {
outOfSync = true;
return;
}
code = state.code;
config = state.mermaid;
panZoomEnabled = state.panZoom;
rough = state.rough;
const scroll = view.parentElement?.scrollTop;
delete container.dataset.processed;
const { svg, bindFunctions } = await renderDiagram(
Object.assign({}, JSON.parse(state.mermaid)) as MermaidConfig,
code,
'graph-div'
);
if (svg.length > 0) {
handlePanZoom(state);
container.innerHTML = svg;
const graphDiv = document.querySelector<SVGSVGElement>('#graph-div');
if (!graphDiv) {
throw new Error('graph-div not found');
}
if (state.rough) {
const svg2roughjs = new Svg2Roughjs('#container');
svg2roughjs.svg = graphDiv;
await svg2roughjs.sketch();
graphDiv.remove();
const sketch = document.querySelector<HTMLElement>('#container > svg');
if (!sketch) {
throw new Error('sketch not found');
}
const height = sketch.getAttribute('height');
const width = sketch.getAttribute('width');
sketch.setAttribute('height', '100%');
sketch.setAttribute('width', '100%');
sketch.setAttribute('viewBox', `0 0 ${width} ${height}`);
sketch.style.maxWidth = '100%';
} else {
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
if (bindFunctions) {
bindFunctions(graphDiv);
}
}
}
if (view.parentElement && scroll) {
view.parentElement.scrollTop = scroll;
}
error = false;
} else if (manualUpdate) {
manualUpdate = false;
} else if (code !== state.code || config !== state.mermaid) {
outOfSync = true;
}
} catch (error_) {
console.error('view fail', error_);
error = true;
}
const renderTime = Date.now() - startTime;
saveStatistics({ code, renderTime, isRough: state.rough });
recordRenderTime(renderTime, () => {
$inputStateStore.updateDiagram = true;
});
};
onMount(() => {
stateStore.subscribe((state) => {
void handleStateChange(state);
});
window.addEventListener('resize', () => {
if ($stateStore.panZoom && pzoom) {
pzoom.resize();
}
});
});
</script>
{#if (error && $stateStore.error instanceof Error) || outOfSync}
<div
class="absolute z-10 w-full p-2 font-mono {error
? 'text-red-600'
: 'text-yellow-600'} bg-base-100 bg-opacity-80 text-left"
id="errorContainer">
{#if error}
{#each errorLines as line}
{line}<br />
{/each}
{:else}
Diagram out of sync. <br />
{#if $stateStore.autoSync}
It will be updated automatically.
{:else}
Press <i class="fas fa-sync" /> (Sync button) or <kbd>{cmdKey} + Enter</kbd> to sync.
{/if}
{/if}
</div>
{/if}
<div id="view" bind:this={view} class="h-full p-2" class:error class:outOfSync>
<div id="container" bind:this={container} class="h-full overflow-auto" class:hide />
</div>
<style>
#view {
flex: 1;
}
#container {
transition: visibility 0.3s;
}
.error,
.outOfSync {
opacity: 0.5;
}
.hide {
visibility: hidden;
}
</style>
-259
View File
@@ -1,259 +0,0 @@
<script lang="ts">
import { browser } from '$app/environment';
import Card from '$lib/components/card/card.svelte';
import { env } from '$lib/util/env';
import { pakoSerde } from '$lib/util/serde';
import { stateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { toBase64 } from 'js-base64';
import moment from 'moment';
const { krokiRendererUrl, rendererUrl } = env;
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
const getFileName = (ext: string) =>
`mermaid-diagram-${moment().format('YYYY-MM-DD-HHmmss')}.${ext}`;
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
svg?.setAttribute('height', `${height}px`);
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
if (!svg) {
svg = getSvgEl();
}
const svgString = svg.outerHTML
.replaceAll('<br>', '<br/>')
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`);
return toBase64(svgString);
};
const exportImage = (event: Event, exporter: Exporter) => {
const canvas: HTMLCanvasElement = document.createElement('canvas');
const svg: HTMLElement = document.querySelector('#container svg');
const box: DOMRect = svg.getBoundingClientRect();
canvas.width = box.width;
canvas.height = box.height;
if (imagemodeselected === 'width') {
const ratio = box.height / box.width;
canvas.width = userimagesize;
canvas.height = userimagesize * ratio;
} else if (imagemodeselected === 'height') {
const ratio = box.width / box.height;
canvas.width = userimagesize * ratio;
canvas.height = userimagesize;
}
const context = canvas.getContext('2d');
context.fillStyle = 'white';
context.fillRect(0, 0, canvas.width, canvas.height);
const image = new Image();
image.onload = exporter(context, image);
image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`;
event.stopPropagation();
event.preventDefault();
};
const getSvgEl = () => {
const svgEl: HTMLElement = document
.querySelector('#container svg')
.cloneNode(true) as HTMLElement;
svgEl.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
.map((l) => l.href)
.find((h) => h && h.includes('font-awesome'));
if (fontAwesomeCdnUrl == null) {
return svgEl;
}
const styleEl = document.createElement('style');
styleEl.innerText = `@import url("${fontAwesomeCdnUrl}");'`;
svgEl.prepend(styleEl);
return svgEl;
};
const simulateDownload = (download: string, href: string): void => {
const a = document.createElement('a');
a.download = download;
a.href = href;
a.click();
a.remove();
};
const downloadImage: Exporter = (context, image) => {
return () => {
const { canvas } = context;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
simulateDownload(
getFileName('png'),
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
);
};
};
const isClipboardAvailable = (): boolean => {
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem') as boolean;
};
const clipboardCopy: Exporter = (context, image) => {
return () => {
const { canvas } = context;
context.drawImage(image, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
try {
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
void navigator.clipboard.write([
/* eslint-disable no-undef */
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
new ClipboardItem({
[blob.type]: blob
})
]);
} catch (error) {
console.error(error);
}
});
};
};
const onCopyClipboard = (event: Event) => {
exportImage(event, clipboardCopy);
logEvent('copyClipboard');
};
const onDownloadPNG = (event: Event) => {
exportImage(event, downloadImage);
logEvent('download', {
type: 'png'
});
};
const onDownloadSVG = () => {
simulateDownload(getFileName('svg'), `data:image/svg+xml;base64,${getBase64SVG()}`);
logEvent('download', {
type: 'svg'
});
};
const onCopyMarkdown = () => {
(document.getElementById('markdown') as HTMLInputElement).select();
document.execCommand('Copy');
logEvent('copyMarkdown');
};
let gistURL = '';
stateStore.subscribe(({ loader }) => {
if (loader?.type === 'gist') {
// @ts-ignore Gist will have url
gistURL = loader.config.url;
}
});
const loadGist = () => {
if (!gistURL) {
alert('Please enter a Gist URL first');
}
window.location.href = `${window.location.pathname}?gist=${gistURL}`;
logEvent('loadGist');
};
let iUrl: string;
let svgUrl: string;
let krokiUrl: string;
let mdCode: string;
let imagemodeselected = 'auto';
let userimagesize = 1080;
let isNetlify = false;
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
isNetlify = true;
}
stateStore.subscribe(({ code, serialized }) => {
iUrl = `${rendererUrl}/img/${serialized}?type=png`;
svgUrl = `${rendererUrl}/svg/${serialized}`;
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
mdCode = `[![](${iUrl})](${window.location.protocol}//${window.location.host}${window.location.pathname}#${serialized})`;
});
</script>
<Card title="Actions" isOpen={true}>
<div class="flex flex-wrap gap-2 m-2">
{#if isClipboardAvailable()}
<button class="action-btn w-full" on:click={onCopyClipboard}
><i class="far fa-copy mr-2" /> Copy Image to clipboard
</button>
{/if}
<button id="downloadPNG" class="action-btn flex-grow" on:click={onDownloadPNG}>
<i class="fas fa-download mr-2" /> PNG
</button>
<button id="downloadSVG" class="action-btn flex-grow" on:click={onDownloadSVG}>
<i class="fas fa-download mr-2" /> SVG
</button>
<a target="_blank" class="flex-grow" href={iUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> PNG
</button>
</a>
<a target="_blank" class="flex-grow" href={svgUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> SVG
</button>
</a>
<a target="_blank" class="flex-grow" href={krokiUrl}>
<button class="action-btn w-full">
<i class="fas fa-external-link-alt mr-2" /> Kroki
</button>
</a>
<div class="flex gap-2 items-center">
PNG size
<label for="autosize">
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} /> Auto
</label>
<label for="width">
<input type="radio" value="width" id="width" bind:group={imagemodeselected} /> Width
</label>
<label for="height">
<input type="radio" value="height" id="height" bind:group={imagemodeselected} /> Height
</label>
{#if imagemodeselected !== 'auto'}
<input
id="height"
class="input"
type="number"
min="3"
max="10000"
bind:value={userimagesize} />
{/if}
</div>
<div class="w-full flex gap-2 items-center">
<input class="input" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
<label for="markdown">
<button class="btn btn-primary btn-md flex-auto" on:click={onCopyMarkdown}>
Copy Markdown
</button>
</label>
</div>
<div class="w-full flex gap-2 items-center">
<input
class="input"
id="gist"
type="text"
bind:value={gistURL}
placeholder="Enter Gist URL" />
<label for="gist">
<button class="btn btn-primary btn-md flex-auto" on:click={loadGist}> Load Gist </button>
</label>
</div>
{#if isNetlify}
<div class="w-full flex items-center justify-center">
<a class="link underline text-gray-500 text-sm" href="https://netlify.com">
This site is powered by Netlify
</a>
</div>
{/if}
</div>
</Card>
@@ -1,3 +0,0 @@
// Vitest Snapshot v1
exports[`card.svelte > mounts 1`] = `"<div><div class=\\"card rounded overflow-hidden m-2 flex-grow flex flex-col shadow-2xl\\"><div class=\\"bg-primary p-2 pb-0 flex-none cursor-pointer\\"><div class=\\"flex justify-between\\"><div class=\\"flex cursor-default s-_wx1E_JHsCoF\\"><span class=\\"mr-2 font-semibold s-_wx1E_JHsCoF\\"><i class=\\"fas fa-chevron-right icon s-_wx1E_JHsCoF isOpen\\"></i> TabTest</span> <ul class=\\"tabs s-_wx1E_JHsCoF\\"><div class=\\"tab tab-lifted tab-active s-_wx1E_JHsCoF\\"><i class=\\"mr-1 undefined s-_wx1E_JHsCoF\\"></i> title1 </div><div class=\\"tab tab-lifted text-primary-content s-_wx1E_JHsCoF\\"><i class=\\"mr-1 undefined s-_wx1E_JHsCoF\\"></i> title2 </div></ul></div><!--<Tabs>--> <div class=\\"flex gap-x-4 items-center -mt-2\\"></div></div></div> <div class=\\"card-body p-0 flex-grow overflow-auto text-base-content\\"></div></div><!--<Card>--></div>"`;
-30
View File
@@ -1,30 +0,0 @@
<script lang="ts">
import type { Tab } from '$lib/types';
import { slide } from 'svelte/transition';
import Tabs from './tabs.svelte';
export let isCloseable = true;
export let isOpen = true;
export let tabs: Tab[] = [];
export let activeTabID: string = '';
export let title: string;
$: isOpen = isCloseable ? isOpen : true;
$: isTabsShown = isOpen && tabs.length > 0;
</script>
<div class="card rounded overflow-hidden m-2 flex-grow flex flex-col shadow-2xl">
<div
class="bg-primary p-2 {isTabsShown ? 'pb-0' : ''} flex-none cursor-pointer"
on:click={() => (isOpen = !isOpen)}>
<div class="flex justify-between">
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} {activeTabID} />
<div class="flex gap-x-4 items-center {isTabsShown ? '-mt-2' : ''}">
<slot name="actions" />
</div>
</div>
</div>
{#if isOpen}
<div class="card-body p-0 flex-grow overflow-auto text-base-content" transition:slide>
<slot />
</div>
{/if}
</div>
-24
View File
@@ -1,24 +0,0 @@
import { cleanup, render } from '@testing-library/svelte';
import { describe, expect, it, afterEach } from 'vitest';
import Card from './card.svelte';
describe('card.svelte', () => {
// TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
afterEach(() => cleanup());
it('mounts', () => {
const { container } = render(Card, {
title: 'TabTest',
tabs: [
{ id: 't1', title: 'title1' },
{ id: 't2', title: 'title2' }
]
});
expect(container).toBeTruthy();
expect(container).toHaveTextContent('TabTest');
expect(container).toHaveTextContent('title1');
expect(container).toHaveTextContent('title2');
expect(container).not.toHaveTextContent('title3');
expect(container.innerHTML).toMatchSnapshot();
});
});
-48
View File
@@ -1,48 +0,0 @@
<script lang="ts">
import type { Tab, TabEvents } from '$lib/types';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
export let isCloseable = true;
export let tabs: Tab[];
export let title: string;
export let isOpen = false;
export let activeTabID: string;
if (!activeTabID && tabs.length > 0) {
activeTabID = tabs[0].id;
}
const dispatch = createEventDispatcher<TabEvents>();
const toggleTabs = (tab: Tab) => {
activeTabID = tab.id;
dispatch('select', tab);
};
</script>
<div class="flex cursor-default">
<span class="mr-2 font-semibold" on:click|stopPropagation={() => (isOpen = !isOpen)}>
{#if isCloseable}
<i class="fas fa-chevron-right icon" class:isOpen />
{/if}
{title}</span>
{#if isOpen && tabs}
<ul class="tabs" transition:fade>
{#each tabs as tab}
<div
class="tab tab-lifted {activeTabID === tab.id ? 'tab-active' : 'text-primary-content'}"
on:click|stopPropagation={() => toggleTabs(tab)}>
<i class="mr-1 {tab.icon}" />
{tab.title}
</div>
{/each}
</ul>
{/if}
</div>
<style>
.icon {
transition-duration: 0.5s;
}
.isOpen {
transform: rotate(90deg);
}
</style>
-103
View File
@@ -1,103 +0,0 @@
<script lang="ts">
import type { EditorMode } from '$lib/types';
import { stateStore, updateCode, updateConfig } from '$lib/util/state';
import { themeStore } from '$lib/util/theme';
import { syncDiagram } from '$lib/util/util';
import type monaco from 'monaco-editor';
import { onMount } from 'svelte';
import initEditor from 'monaco-mermaid';
import { logEvent } from '$lib/util/stats';
let divEl: HTMLDivElement = null;
let editor: monaco.editor.IStandaloneCodeEditor;
let Monaco: typeof monaco;
let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
minimap: {
enabled: false
},
theme: 'mermaid',
overviewRulerLanes: 0
};
let text = '';
stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
if (!editor) return;
// Update editor text if it's different
const newText = editorMode === 'code' ? code : mermaid;
if (newText !== text) {
editor.setValue(newText);
text = newText;
}
// Update editor mode if it's different
const language = editorMode === 'code' ? 'mermaid' : 'json';
if (editor.getModel().getLanguageId() !== language) {
Monaco?.editor.setModelLanguage(editor.getModel(), language);
}
// Display/clear errors
Monaco?.editor.setModelMarkers(editor.getModel(), 'mermaid', errorMarkers);
});
themeStore.subscribe(({ isDark }) => {
editor && Monaco?.editor.setTheme(isDark ? 'mermaid-dark' : 'mermaid');
});
const handleUpdate = (text: string, mode: EditorMode) => {
if (mode === 'code') {
updateCode(text);
} else {
updateConfig(text);
}
};
const loadMonaco = async () => {
console.log('Loading Monaco...');
let i = 0;
while (i++ < 500) {
// @ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
Monaco = window.monaco;
if (Monaco !== undefined) {
return;
}
await new Promise((r) => setTimeout(r, 100));
}
alert('Loading Monaco Editor failed. Please try refreshing the page.');
};
onMount(async () => {
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
initEditor(Monaco);
editor = Monaco.editor.create(divEl, editorOptions);
editor.onDidChangeModelContent(() => {
text = editor.getValue();
handleUpdate(text, $stateStore.editorMode);
});
editor.addAction({
id: 'mermaid-render-diagram',
label: 'Render Diagram',
keybindings: [Monaco.KeyMod.CtrlCmd | Monaco.KeyCode.Enter],
run: function () {
syncDiagram();
logEvent('renderDiagram', {
method: 'keyboadShortcut'
});
}
});
Monaco?.editor.setTheme($themeStore.isDark ? 'mermaid-dark' : 'mermaid');
const resizeObserver = new ResizeObserver((entries) => {
editor.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
resizeObserver.observe(divEl.parentElement);
return () => {
editor.dispose();
};
});
</script>
<div bind:this={divEl} id="editor" class="overflow-hidden" />
-191
View File
@@ -1,191 +0,0 @@
<script lang="ts">
import Card from '$lib/components/card/card.svelte';
import { inputStateStore, getStateString } from '$lib/util/state';
import {
addHistoryEntry,
historyModeStore,
clearHistoryData,
getPreviousState,
historyStore,
loaderHistoryStore,
restoreHistory
} from './history';
import { notify, prompt } from '$lib/util/notify';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import moment from 'moment';
import type { HistoryType, State, Tab } from '$lib/types';
import { logEvent } from '$lib/util/stats';
const HISTORY_SAVE_INTERVAL = 60000;
const tabSelectHandler = (message: CustomEvent<Tab>) => {
historyModeStore.set(message.detail.id as HistoryType);
};
let tabs: Tab[] = [
{
id: 'manual',
title: 'Saved',
icon: 'far fa-bookmark'
},
{
id: 'auto',
title: 'Timeline',
icon: 'fas fa-history'
}
];
const downloadHistory = () => {
const data = get(historyStore);
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `mermaid-history-${moment().format('YYYY-MM-DD-HHmmss')}.json`;
a.click();
URL.revokeObjectURL(url);
logEvent('history', {
action: 'download'
});
};
const uploadHistory = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'application/json';
input.addEventListener('change', ({ target }: Event) => {
const file = (<HTMLInputElement>target).files[0];
if (!file) {
return;
}
const reader = new FileReader();
reader.onload = (e) => {
const data = JSON.parse(e.target.result as string);
restoreHistory(data);
};
reader.readAsText(file);
});
input.click();
};
const saveHistory = (auto = false) => {
const currentState: string = getStateString();
const previousState: string = getPreviousState(auto);
if (previousState !== currentState) {
addHistoryEntry({
state: $inputStateStore,
time: Date.now(),
type: auto ? 'auto' : 'manual'
});
} else if (!auto) {
notify('State already saved.');
}
};
const clearHistory = (id?: string): void => {
if (!id && !prompt('Clear all saved items?')) {
return;
}
clearHistoryData(id);
};
const restoreHistoryItem = (state: State): void => {
inputStateStore.set({ ...state, updateDiagram: true });
};
const relativeTime = (time: number) => {
const t = new Date(time);
return `${new Date(t).toLocaleString()} (${moment(t).fromNow()})`;
};
onMount(() => {
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 = false;
</script>
<Card on:select={tabSelectHandler} bind:isOpen {tabs} title="History">
<div slot="actions">
<button
id="uploadHistory"
class="btn btn-xs btn-secondary w-12"
on:click|stopPropagation={() => uploadHistory()}
title="Upload history"><i class="fa fa-upload" /></button>
{#if $historyStore.length > 0}
<button
id="downloadHistory"
class="btn btn-xs btn-secondary w-12"
on:click|stopPropagation={() => downloadHistory()}
title="Download history"><i class="fa fa-download" /></button>
{/if}
|
<button
id="saveHistory"
class="btn btn-xs btn-success w-12"
on:click|stopPropagation={() => saveHistory()}
title="Save current state"><i class="far fa-save" /></button>
{#if $historyModeStore !== 'loader'}
<button
id="clearHistory"
class="btn btn-xs btn-error w-12"
on:click|stopPropagation={() => clearHistory()}
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
{/if}
</div>
<ul class="p-2 space-y-2 overflow-auto h-56" id="historyList">
{#if $historyStore.length > 0}
{#each $historyStore as { id, state, time, name, url, type }}
<li class="rounded p-2 shadow flex-col">
<div class="flex">
<div class="flex-1">
<div class="flex flex-col text-base-content">
{#if url}
<a
href={url}
target="_blank"
title="Open revision in new tab"
class="hover:underline text-blue-500">{name}</a>
{:else}
<span>{name}</span>
{/if}
<span class="text-gray-400 text-sm">{relativeTime(time)}</span>
</div>
</div>
<div class="flex gap-2 content-center">
<button class="btn btn-success" on:click={() => restoreHistoryItem(state)}
><i class="fas fa-undo mr-1" />Restore</button>
{#if type !== 'loader'}
<button class="btn btn-error" on:click={() => clearHistory(id)}
><i class="fas fa-trash-alt mr-1" />Delete</button>
{/if}
</div>
</div>
</li>
{/each}
{:else}
<div class="m-2">
No items in History<br />
Click the Save button to save current state and restore it later.<br />
Timeline will automatically be saved every minute.
</div>
{/if}
</ul>
</Card>
-120
View File
@@ -1,120 +0,0 @@
import type { HistoryEntry } from '$lib/types';
import { describe, it, expect } from 'vitest';
import {
addHistoryEntry,
injectHistoryIDs,
clearHistoryData,
historyModeStore,
historyStore
} from './history';
import { defaultState } from '../../util/state';
import { get } from 'svelte/store';
describe('history', () => {
it('should handle saving individual history entry', () => {
expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
addHistoryEntry({
state: defaultState,
time: 12345,
type: 'manual'
});
const [manualEntry]: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
expect(manualEntry.time).toBe(12345);
expect(manualEntry.type).toBe('manual');
expect(manualEntry.name).not.toBeNull();
expect(manualEntry.state).not.toBeNull();
addHistoryEntry({
state: defaultState,
time: 54321,
type: 'auto'
});
const [autoEntry]: HistoryEntry[] = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
expect(autoEntry.time).toBe(54321);
expect(autoEntry.type).toBe('auto');
expect(autoEntry.name).not.toBeNull();
expect(autoEntry.state).not.toBeNull();
historyModeStore.set('manual');
clearHistoryData();
historyModeStore.set('auto');
clearHistoryData();
expect(window.localStorage.getItem('manualHistoryStore')).toBe('[]');
expect(window.localStorage.getItem('autoHistoryStore')).toBe('[]');
});
it('should clear history entries', () => {
addHistoryEntry({
state: defaultState,
time: 12345,
type: 'manual'
});
addHistoryEntry({
state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
time: 123456,
type: 'manual'
});
historyModeStore.set('manual');
const store: HistoryEntry[] = get(historyStore);
expect(store.length).toBe(2);
clearHistoryData(store[1].id);
expect(get(historyStore).length).toBe(1);
clearHistoryData();
expect(get(historyStore).length).toBe(0);
historyModeStore.set('auto');
addHistoryEntry({
state: defaultState,
time: 54321,
type: 'auto'
});
addHistoryEntry({
state: { ...defaultState, code: 'graph TD\\n A[Christmas] -->|Get money| B(Go shopping)' },
time: 654321,
type: 'auto'
});
expect(get(historyStore).length).toBe(2);
clearHistoryData();
expect(get(historyStore).length).toBe(0);
// Test calling when history is empty
clearHistoryData();
expect(get(historyStore).length).toBe(0);
});
});
describe('history migration', () => {
it('should inject history IDs as migration', () => {
window.localStorage.setItem(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
);
window.localStorage.setItem(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
injectHistoryIDs();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
});
-141
View File
@@ -1,141 +0,0 @@
import { derived, writable, get } from 'svelte/store';
import type { Readable, Writable } from 'svelte/store';
import { persist, localStorage } from '$lib/util/persist';
import { generateSlug } from 'random-word-slugs';
import type { HistoryEntry, HistoryType, Optional } from '$lib/types';
import { v4 as uuidV4 } from 'uuid';
import { logEvent } from '$lib/util/stats';
const MAX_AUTO_HISTORY_LENGTH = 30;
export const historyModeStore: Writable<HistoryType> = persist(
writable('manual'),
localStorage(),
'autoHistoryMode'
);
const autoHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
'autoHistoryStore'
);
const manualHistoryStore: Writable<HistoryEntry[]> = persist(
writable([]),
localStorage(),
'manualHistoryStore'
);
export const loaderHistoryStore: Writable<HistoryEntry[]> = writable([] as HistoryEntry[]);
export const historyStore: Readable<HistoryEntry[]> = derived(
[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 = (entryToAdd: Optional<HistoryEntry, 'id'>): void => {
const entry: HistoryEntry = {
...entryToAdd,
id: uuidV4()
};
if (entry.type === 'loader') {
loaderHistoryStore.update((entries) => [entry, ...entries]);
return;
}
if (!entry.name) {
entry.name = generateSlug(2);
}
if (entry.type === 'auto') {
autoHistoryStore.update((entries) => {
if (entries.length >= MAX_AUTO_HISTORY_LENGTH) {
entries = entries.slice(0, MAX_AUTO_HISTORY_LENGTH - 1);
}
return [entry, ...entries];
});
} else if (entry.type === 'manual') {
manualHistoryStore.update((entries) => [entry, ...entries]);
logEvent('history', { action: 'save' });
}
};
export const clearHistoryData = (idToClear?: string): void => {
(get(historyModeStore) === 'auto' ? autoHistoryStore : manualHistoryStore).update((entries) => {
if (get(historyModeStore) !== 'loader') {
entries = entries.filter(({ id }) => idToClear && id != idToClear);
logEvent('history', { action: 'clear', type: idToClear ? 'single' : 'all' });
}
return entries;
});
};
export const getPreviousState = (auto: boolean): string => {
const entries = get(auto ? autoHistoryStore : manualHistoryStore);
if (entries.length > 0) {
return JSON.stringify(entries[0].state);
}
return '';
};
export const restoreHistory = (data: HistoryEntry[]) => {
const entries = data.filter(validateEntry);
const invalidEntryCount = data.length - entries.length;
if (invalidEntryCount > 0) {
console.error(`${invalidEntryCount} invalid history entries were removed.`);
console.error(data);
}
if (entries.length > 0) {
let entryCount = 0;
(entries[0].type === 'auto' ? autoHistoryStore : manualHistoryStore).update((existing) => {
const existingIDs = new Set(existing.map(({ id }) => id));
const newEntries = entries.filter(({ id }) => !existingIDs.has(id));
entryCount = newEntries.length;
const combined = [...existing, ...newEntries];
combined.sort((a, b) => b.time - a.time);
return combined;
});
alert(
`${entryCount} entries restored. ${invalidEntryCount} invalid, ${
entries.length - entryCount
} duplicates.`
);
logEvent('history', {
action: 'restore',
success: entryCount,
invalid: invalidEntryCount,
duplicates: entries.length - entryCount
});
} else {
alert('No valid entries found.');
}
};
export const injectHistoryIDs = (): void => {
const setIDs = (entries: HistoryEntry[]) => {
for (const entry of entries) {
if (!entry.id) {
entry.id = uuidV4();
}
}
return entries;
};
autoHistoryStore.update(setIDs);
manualHistoryStore.update(setIDs);
};
const validateEntry = (entry: HistoryEntry): boolean => {
return entry.type && entry.state && entry.time && true;
};
-82
View File
@@ -1,82 +0,0 @@
<script context="module">
import { version } from 'mermaid/package.json';
import { analytics } from '$lib/util/stats';
analytics?.track('version', {
mermaidVersion: version
});
</script>
<script lang="ts">
import Theme from './theme.svelte';
interface Link {
title: string;
href: string;
icon?: string;
}
const links: Link[] = [
{
title: 'Documentation',
href: 'https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted'
},
{
title: 'Tutorial',
href: 'https://github.com/mermaid-js/mermaid/blob/develop/docs/Tutorials.md'
},
{
title: 'Mermaid',
href: 'https://github.com/mermaid-js/mermaid'
},
{
title: 'CLI',
href: 'https://github.com/mermaid-js/mermaid-cli'
},
{
title: '',
href: 'https://github.com/mermaid-js/mermaid-live-editor',
icon: 'fab fa-github fa-lg'
}
];
</script>
<div class="navbar shadow-lg bg-primary p-0">
<div class="flex-1 px-2 mx-2">
<span class="text-lg font-bold">
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
</span>
</div>
<label for="menu-toggle" class="pointer-cursor lg:hidden block"
><svg
class="fill-current "
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
><title>Menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg
></label>
<input class="hidden" type="checkbox" id="menu-toggle" />
<Theme />
<div class="hidden lg:flex lg:items-center lg:w-auto w-full" id="menu">
<ul class="lg:flex items-center justify-between text-base pt-4 lg:pt-0">
{#each links as { title, href, icon }}
<li>
<a class="btn btn-ghost" target="_blank" {href}>
{#if icon}
<i class={icon} />
{/if}
{title}</a>
</li>
{/each}
</ul>
</div>
</div>
<style>
#menu-toggle:checked + #menu {
display: block;
}
.navbar {
z-index: 10000;
}
</style>
-114
View File
@@ -1,114 +0,0 @@
<script lang="ts">
import { updateCode } from '$lib/util/state';
import Card from '$lib/components/card/card.svelte';
import { logEvent } from '$lib/util/stats';
const samples = {
'Flow Chart': `graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]`,
'Sequence Diagram': `sequenceDiagram
Alice->>+John: Hello John, how are you?
Alice->>+John: John, can you hear me?
John-->>-Alice: Hi Alice, I can hear you!
John-->>-Alice: I feel great!
`,
'Class Diagram': `classDiagram
Animal <|-- Duck
Animal <|-- Fish
Animal <|-- Zebra
Animal : +int age
Animal : +String gender
Animal: +isMammal()
Animal: +mate()
class Duck{
+String beakColor
+swim()
+quack()
}
class Fish{
-int sizeInFeet
-canEat()
}
class Zebra{
+bool is_wild
+run()
}
`,
'State Diagram': `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]
`,
'Gantt Chart': `gantt
title A Gantt Diagram
dateFormat YYYY-MM-DD
section Section
A task :a1, 2014-01-01, 30d
Another task :after a1 , 20d
section Another
Task in sec :2014-01-12 , 12d
another task : 24d
`,
'Pie Chart': `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15
`,
'ER Diagram': `erDiagram
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
CUSTOMER ||--o{ ORDER : places
CUSTOMER ||--o{ INVOICE : "liable for"
DELIVERY-ADDRESS ||--o{ ORDER : receives
INVOICE ||--|{ ORDER : covers
ORDER ||--|{ ORDER-ITEM : includes
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
`,
'User Journey': ` journey
title My working day
section Go to work
Make tea: 5: Me
Go upstairs: 3: Me
Do work: 1: Me, Cat
section Go home
Go downstairs: 5: Me
Sit down: 3: Me
`,
'Git Graph': ` gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
`
};
const loadSampleDiagram = (diagramType: string): void => {
updateCode(samples[diagramType], {
updateDiagram: true,
resetPanZoom: true
});
logEvent('loadSampleDiagram', { diagramType });
};
</script>
<Card title="Sample Diagrams" isOpen={false}>
<div class="flex gap-2 flex-wrap p-2">
{#each Object.keys(samples) as sample}
<button class="btn btn-primary normal-case btn-sm" on:click={() => loadSampleDiagram(sample)}
>{sample}</button>
{/each}
</div>
</Card>
-59
View File
@@ -1,59 +0,0 @@
<script>
import { setTheme, themeStore } from '$lib/util/theme';
const themes = [
'🌝 light',
'🌚 dark',
'🧁 cupcake',
'🐝 bumblebee',
'✳️ emerald',
'🏢 corporate',
'🌃 synthwave',
'👴 retro',
'🤖 cyberpunk',
'🌸 valentine',
'🎃 halloween',
'🌷 garden',
'🌲 forest',
'🐟 aqua',
'👓 lofi',
'🖍 pastel',
'🧚‍♀️ fantasy',
'📝 wireframe',
'🏴 black',
'💎 luxury',
'🧛‍♂️ dracula'
];
</script>
<div class="hidden lg:block dropdown">
<div tabindex="0" class="btn btn-ghost ">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="inline-block w-6 h-6 stroke-current md:mr-2"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>
<span class="hidden md:inline"> Change Theme </span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1792 1792"
class="inline-block w-4 h-4 ml-1 fill-current"
><path
d="M1395 736q0 13-10 23l-466 466q-10 10-23 10t-23-10l-466-466q-10-10-10-23t10-23l50-50q10-10 23-10t23 10l393 393 393-393q10-10 23-10t23 10l50 50q10 10 10 23z" /></svg>
</div>
<div
class="mt-14 overflow-y-auto shadow-2xl top-px dropdown-content h-96 w-56 bg-base-200 text-base-content">
<ul tabindex="0" class="p-4 menu compact">
{#each themes as theme}
<li class={theme.includes($themeStore.theme) ? 'bordered' : ''}>
<span class="btn btn-ghost justify-start" on:click={() => setTheme(theme)}>{theme}</span>
</li>
{/each}
</ul>
</div>
</div>
-148
View File
@@ -1,148 +0,0 @@
<script lang="ts">
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { onMount } from 'svelte';
import panzoom from 'svg-pan-zoom';
import type { State, ValidatedState } from '$lib/types';
import { logEvent } from '$lib/util/stats';
import { cmdKey } from '$lib/util/util';
import { render as renderDiagram } from '$lib/util/mermaid';
let code = '';
let config = '';
let container: HTMLDivElement;
let view: HTMLDivElement;
let error = false;
let outOfSync = false;
let hide = false;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let pzoom: SvgPanZoom.Instance;
const handlePanZoomChange = () => {
const pan = pzoom.getPan();
const zoom = pzoom.getZoom();
updateCodeStore({ pan, zoom });
logEvent('panZoom');
};
const handlePanZoom = (state: State) => {
if (!state.panZoom) {
return;
}
hide = true;
pzoom?.destroy();
pzoom = undefined;
Promise.resolve().then(() => {
const graphDiv = document.getElementById('graph-div');
pzoom = panzoom(graphDiv, {
onPan: handlePanZoomChange,
onZoom: handlePanZoomChange,
controlIconsEnabled: true,
fit: true,
center: true
});
const { pan, zoom } = state;
if (pan !== undefined && zoom !== undefined && Number.isFinite(zoom)) {
pzoom.zoom(zoom);
pzoom.pan(pan);
}
hide = false;
});
};
const handleStateChange = async (state: ValidatedState) => {
if (state.error !== undefined) {
error = true;
return;
}
error = false;
try {
if (container && state && (state.updateDiagram || state.autoSync)) {
if (!state.autoSync) {
$inputStateStore.updateDiagram = false;
}
outOfSync = false;
manualUpdate = true;
if (code === state.code && config === state.mermaid && panZoomEnabled === state.panZoom) {
// Do not render if there is no change in Code/Config/PanZoom
return;
}
code = state.code;
config = state.mermaid;
panZoomEnabled = state.panZoom;
const scroll = view.parentElement.scrollTop;
delete container.dataset.processed;
await renderDiagram(
Object.assign({}, JSON.parse(state.mermaid)),
code,
'graph-div',
(svgCode, bindFunctions) => {
if (svgCode.length > 0) {
handlePanZoom(state);
container.innerHTML = svgCode;
const graphDiv = document.getElementById('graph-div');
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
if (bindFunctions) {
bindFunctions(graphDiv);
}
}
}
);
view.parentElement.scrollTop = scroll;
error = false;
} else if (manualUpdate) {
manualUpdate = false;
} else if (code !== state.code || config !== state.mermaid) {
outOfSync = true;
}
} catch (e) {
console.error('view fail', e);
error = true;
}
};
onMount(() => {
stateStore.subscribe(async (state) => {
await handleStateChange(state);
});
window.addEventListener('resize', () => {
if ($stateStore.panZoom && pzoom) {
pzoom.resize();
}
});
});
</script>
{#if error && $stateStore.error instanceof Error}
<div class="p-2 text-red-600" id="errorContainer">{$stateStore.error}</div>
{/if}
{#if outOfSync}
<div class="absolute w-full p-2 z-10 text-yellow-600 bg-base-100 bg-opacity-80 text-center">
Diagram out of sync. <br />
Press <i class="fas fa-sync" /> (Sync button) or <kbd>{cmdKey} + Enter</kbd> to sync.
</div>
{/if}
<div id="view" bind:this={view} class="p-2 h-full" class:error class:outOfSync>
<div id="container" bind:this={container} class="h-full overflow-auto" class:hide />
</div>
<style>
#view {
flex: 1;
}
#container {
transition: visibility 0.3s;
}
.error,
.outOfSync {
opacity: 0.5;
}
.hide {
visibility: hidden;
}
</style>
+56 -53
View File
@@ -1,86 +1,89 @@
/**
* Can be made globally available by placing this
* inside `global.d.ts` and removing `export` keyword
*/
export interface Locals {
userid: string;
}
export interface MarkerData {
severity: number;
message: string;
source?: string;
startLineNumber: number;
startColumn: number;
endLineNumber: number;
endColumn: number;
severity: number;
message: string;
source?: string;
startLineNumber: number;
startColumn: number;
endLineNumber: number;
endColumn: number;
}
export interface TabEvents {
select: Tab;
select: Tab;
}
export interface Tab {
id: string;
title: string;
icon: string;
id: string;
title: string;
icon: string;
}
export interface State {
code: string;
mermaid: string;
updateDiagram: boolean;
autoSync: boolean;
editorMode?: EditorMode;
panZoom?: boolean;
pan?: { x: number; y: number };
zoom?: number;
loader?: LoaderConfig;
code: string;
mermaid: string;
updateDiagram: boolean;
autoSync: boolean;
rough: boolean;
editorMode?: EditorMode;
panZoom?: boolean;
pan?: { x: number; y: number };
zoom?: number;
loader?: LoaderConfig;
}
export interface ValidatedState extends State {
editorMode: EditorMode;
error: unknown;
errorMarkers: MarkerData[];
serialized: string;
editorMode: EditorMode;
error?: Error;
errorMarkers: MarkerData[];
serialized: string;
}
export interface GistLoaderConfig {
url: string;
url: string;
}
export interface LoadingState {
loading: boolean;
message?: string;
loading: boolean;
message?: string;
}
export interface FileLoaderConfig {
codeURL: string;
configURL?: string;
codeURL: string;
configURL?: string;
}
export interface LoaderConfig {
type: 'gist' | 'files';
config: GistLoaderConfig | FileLoaderConfig;
type: 'gist' | 'files';
config: GistLoaderConfig | FileLoaderConfig;
}
export type HistoryType = 'auto' | 'manual' | 'loader';
export type HistoryEntry = { id: string; state: State; time: number; url?: string } & (
| {
type: 'loader';
name: string;
}
| {
type: HistoryType;
name?: string;
}
| {
type: 'loader';
name: string;
}
| {
type: HistoryType;
name?: string;
}
);
export interface DocConfig {
[key: string]: {
code: string;
config?: string;
};
}
export type DocumentationConfig = Record<
string,
{
code: string;
config?: string;
}
>;
export type EditorMode = 'code' | 'config';
export type Loader = (url: string) => Promise<State>;
export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export interface ErrorHash {
loc: {
first_line: number;
last_line: number;
first_column: number;
last_column: number;
};
}
+49
View File
@@ -0,0 +1,49 @@
import debounce from 'lodash-es/debounce';
import { get } from 'svelte/store';
import { stateStore } from './state';
let shouldSync = true;
let updater: () => void;
let renderPromise: Promise<void> | undefined;
let resolveRenderPromise: (() => void) | undefined;
const renderDelay = 1000;
const slowRenderThreshold = 150;
const debouncedRender = debounce(() => {
shouldSync = true;
updater();
}, renderDelay);
export const recordRenderTime = (renderTimeMs: number, updaterFunction: () => void): void => {
resolveRenderPromise?.();
const { autoSync } = get(stateStore);
if (!autoSync) {
return;
}
updater = updaterFunction;
const isSlow = renderTimeMs > slowRenderThreshold;
if (!shouldSync) {
debouncedRender();
}
shouldSync = !isSlow;
};
export const shouldRefreshView = (): boolean => {
if (!renderPromise) {
renderPromise = new Promise((resolve) => {
resolveRenderPromise = () => {
renderPromise = undefined;
resolve();
};
});
}
if (!shouldSync) {
debouncedRender();
}
return shouldSync;
};
export const waitForRender = (): Promise<void> => {
return renderPromise ?? Promise.resolve();
};
+5 -8
View File
@@ -1,9 +1,6 @@
export const env = {
rendererUrl: import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink',
krokiRendererUrl: import.meta.env.MERMAID_KROKI_RENDERER_URL ?? 'https://kroki.io',
mermaidCDNUrl: import.meta.env.MERMAID_CDN_URL ?? 'https://unpkg.com/@mermaid-js',
mermaidBaseURL: import.meta.env.MERMAID_BASE_URL ?? 'http://localhost:9000',
useLocalMermaid: import.meta.env.MERMAID_LOCAL ?? false,
isDev: import.meta.env.DEV,
baseURL: import.meta.env.BASE_URL
};
rendererUrl: import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink',
krokiRendererUrl: import.meta.env.MERMAID_KROKI_RENDERER_URL ?? 'https://kroki.io',
analyticsUrl: import.meta.env.MERMAID_ANALYTICS_URL ?? '',
domain: import.meta.env.MERMAID_DOMAIN ?? 'mermaid.live'
} as const;
+106 -82
View File
@@ -2,106 +2,130 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { addHistoryEntry } from '$lib/components/History/history';
import type { State } from '$lib/types';
import { defaultState } from '../state';
import { addHistoryEntry } from '../../components/history/history';
import { defaultState } from '$lib/util/state';
import { fetchJSON, fetchText } from '$lib/util/util';
const codeFileName = 'code.mmd';
const configFileName = 'config.json';
interface GithubFile {
truncated: boolean;
raw_url: string;
content: string;
}
const isValidGist = (files: any): boolean => {
return codeFileName in files;
return codeFileName in files;
};
const getFileContent = async (file: any): Promise<string> => {
if (file.truncated) {
return await (await fetch(file.raw_url)).text();
}
return file.content;
const getFileContent = async (file: GithubFile): Promise<string> => {
if (file.truncated) {
return await fetchText(file.raw_url);
}
return file.content;
};
interface GistData {
code: string;
config?: string;
author: string;
time: number;
version: string;
url: string;
code: string;
config?: string;
author: string;
time: number;
version: string;
url: string;
}
interface GistResponse {
files: Record<string, GithubFile>;
html_url: string;
history: { url: string; committed_at: string; version: string; user: { login: string } }[];
}
const getGistData = async (gistURL: string): Promise<GistData> => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
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]);
let config: string;
if (configFileName in files) {
config = await getFileContent(files[configFileName]);
}
const currentItem = history[0];
return {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
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 path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { html_url, files, history }: GistResponse = await fetchJSON(
`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`
);
if (isValidGist(files)) {
const code = await getFileContent(files[codeFileName]);
let config = '{}';
if (configFileName in files) {
config = await getFileContent(files[configFileName]);
}
const currentItem = history[0];
return {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
url: `${html_url}/${currentItem.version}`,
code,
config,
author: currentItem.user.login,
time: new Date(currentItem.committed_at).getTime(),
version: currentItem.version.slice(-7)
};
} else {
throw new Error('Invalid gist provided');
}
};
const getStateFromGist = (gist: GistData, gistURL: string = gist.url): State => {
const state: State = {
...defaultState,
code: gist.code,
loader: {
type: 'gist',
config: {
url: gistURL
}
}
};
gist.config && (state.mermaid = gist.config);
return state;
const state: State = {
...defaultState,
code: gist.code,
loader: {
type: 'gist',
config: {
url: gistURL
}
}
};
gist.config && (state.mermaid = gist.config);
return state;
};
export const loadGistData = async (gistURL: string): Promise<State> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
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(), gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
} catch (err) {
console.error(err);
}
const path = gistURL.split('github.com').pop();
if (!path) {
throw new Error('Invalid GitHub URL' + gistURL);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, __, gistID, revisionID] = path.split('/');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { history }: GistResponse = await fetchJSON(
`https://api.github.com/gists/${gistID}${revisionID ? '/' + revisionID : ''}`
);
const gistHistory: GistData[] = [];
for (const entry of history) {
try {
const data: GistData = await getGistData(entry.url);
gistHistory.push(data);
} catch (error) {
console.error(error);
}
}
if (gistHistory.length === 0) {
throw new Error('Invalid gist provided');
}
gistHistory.reverse();
const entry = gistHistory.at(-1);
if (!entry) {
throw new Error('Invalid gist provided');
}
const state = getStateFromGist(entry, gistURL);
for (const gist of gistHistory) {
addHistoryEntry({
state: getStateFromGist(gist),
time: gist.time,
type: 'loader',
url: gist.url,
name: `${gist.author} v${gist.version}`
});
}
return state;
};
+50 -51
View File
@@ -1,59 +1,58 @@
import { loadGistData } from './gist';
import { updateCodeStore, defaultState } from '../state';
import { updateCodeStore, defaultState } from '$lib/util/state';
import { fetchText } from '$lib/util/util';
import type { Loader, State } from '$lib/types';
const loaders: Record<string, Loader> = {
gist: loadGistData
gist: loadGistData
};
export const loadDataFromUrl = async (): Promise<void> => {
const searchParams = new URLSearchParams(window.location.search);
let state: Partial<State> = defaultState;
let code: string, config: string;
let loaded = false;
const codeURL: string = searchParams.get('code');
const configURL: string = searchParams.get('config');
const searchParams = new URLSearchParams(window.location.search);
let state: Partial<State> = defaultState;
let loaded = false;
const codeURL: string | undefined = searchParams.get('code') ?? undefined;
const configURL: string | undefined = searchParams.get('config') ?? undefined;
if (codeURL) {
code = await (await fetch(codeURL)).text();
loaded = true;
}
if (configURL) {
config = await (await fetch(configURL)).text();
} else {
config = defaultState.mermaid;
}
if (!code) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
for (const [key, value] of searchParams.entries()) {
if (key in loaders) {
try {
state = await loaders[key](value);
loaded = true;
break;
} catch (err) {
console.error(err);
}
}
}
} else {
state = {
code,
mermaid: config,
loader: {
type: 'files',
config: {
codeURL,
configURL
}
}
};
}
loaded &&
updateCodeStore({
...state,
autoSync: true,
updateDiagram: true
});
let code: string | undefined;
const config = configURL ? await fetchText(configURL) : defaultState.mermaid;
if (codeURL) {
code = await fetchText(codeURL);
loaded = true;
}
if (code) {
if (!codeURL) {
throw new Error('Code URL is not defined');
}
state = {
code,
mermaid: config,
loader: {
type: 'files',
config: {
codeURL,
configURL
}
}
};
} else {
for (const [key, value] of searchParams.entries()) {
if (key in loaders) {
try {
state = await loaders[key](value);
loaded = true;
break;
} catch (error) {
console.error(error);
}
}
}
}
loaded &&
updateCodeStore({
...state,
autoSync: true,
updateDiagram: true
});
};
+10 -10
View File
@@ -3,18 +3,18 @@ import type { Writable } from 'svelte/store';
import type { LoadingState } from '$lib/types';
const defaultLoading: LoadingState = {
loading: false
loading: false
};
export const loadingStateStore: Writable<LoadingState> = writable(defaultLoading);
export const initLoading = async <T>(message: string, task: Promise<T>): Promise<T> => {
loadingStateStore.set({
loading: true,
message
});
const result: T = await task;
loadingStateStore.set({
loading: false
});
return result;
loadingStateStore.set({
loading: true,
message
});
const result: T = await task;
loadingStateStore.set({
loading: false
});
return result;
};
+26 -40
View File
@@ -1,47 +1,33 @@
import mermaid from 'mermaid';
// We need to export MermaidConfig and all related types from mermaid.
import type { MermaidConfig } from 'mermaid/dist/config.type';
import { env } from './env';
const { mermaidBaseURL, mermaidCDNUrl, useLocalMermaid } = env;
import type { MermaidConfig, RenderResult } from 'mermaid';
import zenuml from '@mermaid-js/mermaid-zenuml';
const getDiagramURL = (name: string, version: string): string => {
if (useLocalMermaid) {
return `${mermaidBaseURL}/${name}-detector.esm.mjs`;
}
return `${mermaidCDNUrl}/${name}@${version}/dist/${name}-detector.esm.mjs`;
};
console.log(mermaid);
const initialize = mermaid.initializeAsync({
logLevel: 0,
lazyLoadedDiagrams: [getDiagramURL('mermaid-mindmap', '9.2.0-rc4')],
loadExternalDiagramsAtStartup: true
});
export const init = async () => {
await initialize;
};
const init = mermaid.registerExternalDiagrams([zenuml]);
export const render = async (
config: MermaidConfig,
code: string,
id: string,
callback: Parameters<typeof mermaid.render>[2]
): Promise<void> => {
await init();
// Should be able to call this multiple times without any issues.
// await mermaid.initialize({
// ...config,
// lazyLoadedDiagrams: [
// // We should make SRI mandatory for all lazy-loaded diagrams.
// 'https://unpkg.com/@mermaid-js/mermaid-mindmap@9.2.0-rc2/dist/mermaid-mindmap-detector.esm.mjs'
// ]
// });
console.log('Rendering', code);
await mermaid.mermaidAPI.renderAsync(id, code, callback);
config: MermaidConfig,
code: string,
id: string
): Promise<RenderResult> => {
await init;
// Should be able to call this multiple times without any issues.
mermaid.initialize(config);
try {
const result = await mermaid.render(id, code);
return result;
} catch {
const errorDiv = document.querySelector(`#d${id}`);
const svg = errorDiv?.innerHTML ?? '';
errorDiv?.remove();
return {
svg,
bindFunctions: undefined
};
}
};
export const parse = async (code: string): Promise<boolean> => {
await init();
return mermaid.parseAsync(code);
export const parse = async (code: string): Promise<unknown> => {
return await mermaid.parse(code);
};
+30 -26
View File
@@ -2,33 +2,37 @@ import type { HistoryEntry } from '$lib/types';
import { describe, it, expect, beforeEach } from 'vitest';
describe('migrations', () => {
beforeEach(() => {
window.localStorage.setItem(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
);
window.localStorage.setItem(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
});
beforeEach(() => {
window.localStorage.setItem(
'manualHistoryStore',
'[{"state":{"code":"graph TD\\n A[Halloween] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"manual","name":"hollow-art"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"helpful-ocean"}]'
);
window.localStorage.setItem(
'autoHistoryStore',
'[{"state":{"code":"graph TD\\n A[New Year] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":false},"time":0,"type":"auto","name":"barking-dog"},{"state":{"code":"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)","mermaid":"{\\n \\"theme\\": \\"dark\\"\\n}","autoSync":true,"updateDiagram":true},"time":0,"type":"manual","name":"needy-mosquito"}]'
);
});
it('should migrate from v0 to v1', async () => {
const { applyMigrations } = await import('./migrations');
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore')
);
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore')
);
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
it('should migrate from v0 to v1', async () => {
const { applyMigrations } = await import('./migrations');
let manualHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
let autoHistoryStore: HistoryEntry[] = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(false);
applyMigrations();
applyMigrations();
manualHistoryStore = JSON.parse(window.localStorage.getItem('manualHistoryStore'));
autoHistoryStore = JSON.parse(window.localStorage.getItem('autoHistoryStore'));
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
manualHistoryStore = JSON.parse(
window.localStorage.getItem('manualHistoryStore') ?? '[]'
) as HistoryEntry[];
autoHistoryStore = JSON.parse(
window.localStorage.getItem('autoHistoryStore') ?? '[]'
) as HistoryEntry[];
expect(manualHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
expect(autoHistoryStore.every(({ id }) => id !== undefined)).toBe(true);
});
});
+21 -21
View File
@@ -1,35 +1,35 @@
import { writable, get, type Writable } from 'svelte/store';
import { persist, localStorage } from '$lib/util/persist';
import { injectHistoryIDs } from '$lib/components/history/history';
import { injectHistoryIDs } from '$lib/components/History/history';
import { logEvent } from './stats';
interface MigrationState {
version: number;
version: number;
}
const migrations: { [key: string]: () => void } = {
injectHistoryIDs
const migrations: Record<string, () => void> = {
injectHistoryIDs
};
const migrationStore: Writable<MigrationState> = persist(
writable({ version: -1 }),
localStorage(),
'migrations'
writable({ version: -1 }),
localStorage(),
'migrations'
);
export const applyMigrations = (): void => {
const { version }: MigrationState = get(migrationStore);
const allMigrations = Object.entries(migrations);
if (version === allMigrations.length - 1) {
return;
}
console.log(`Current migration version: v${version}. Migrating to v${allMigrations.length - 1}.`);
for (let i = version + 1; i < allMigrations.length; i++) {
const [key, fn] = allMigrations[i];
console.log(`Applying migration ${i}: ${key}.`);
fn();
logEvent('migration', { key });
migrationStore.set({ version: i });
}
logEvent('migration', { status: 'complete', from: version, to: allMigrations.length - 1 });
const { version }: MigrationState = get(migrationStore);
const allMigrations = Object.entries(migrations);
if (version === allMigrations.length - 1) {
return;
}
console.log(`Current migration version: v${version}. Migrating to v${allMigrations.length - 1}.`);
for (let i = version + 1; i < allMigrations.length; i++) {
const [key, fn] = allMigrations[i];
console.log(`Applying migration ${i}: ${key}.`);
fn();
logEvent('migration', { key });
migrationStore.set({ version: i });
}
logEvent('migration', { status: 'complete', from: version, to: allMigrations.length - 1 });
};
+621
View File
@@ -0,0 +1,621 @@
// Modified from https://github.com/Yash-Singh1/monaco-mermaid/blob/main/index.ts
import type * as Monaco from 'monaco-editor';
export const initEditor = (monacoEditor: typeof Monaco): void => {
monacoEditor.languages.register({ id: 'mermaid' });
const requirementDiagrams = [
'requirement',
'functionalRequirement',
'interfaceRequirement',
'performanceRequirement',
'physicalRequirement',
'designConstraint'
];
const keywords: Record<
string,
{
typeKeywords: string[];
blockKeywords: string[];
keywords: string[];
}
> = {
flowchart: {
typeKeywords: ['flowchart', 'flowchart-v2', 'graph'],
blockKeywords: ['subgraph', 'end'],
keywords: [
'TB',
'TD',
'BT',
'RL',
'LR',
'click',
'call',
'href',
'_self',
'_blank',
'_parent',
'_top',
'linkStyle',
'style',
'classDef',
'class',
'direction',
'interpolate'
]
},
sequenceDiagram: {
typeKeywords: ['sequenceDiagram'],
blockKeywords: ['alt', 'par', 'and', 'loop', 'else', 'end', 'rect', 'opt', 'alt', 'rect'],
keywords: [
'participant',
'as',
'Note',
'note',
'right of',
'left of',
'over',
'activate',
'deactivate',
'autonumber',
'title',
'actor',
'accDescription',
'link',
'links'
]
},
classDiagram: {
typeKeywords: ['classDiagram', 'classDiagram-v2'],
blockKeywords: ['class'],
keywords: [
'link',
'click',
'callback',
'call',
'href',
'cssClass',
'direction',
'TB',
'BT',
'RL',
'LR',
'title',
'accDescription',
'order'
]
},
stateDiagram: {
typeKeywords: ['stateDiagram', 'stateDiagram-v2'],
blockKeywords: ['state', 'note', 'end'],
keywords: ['state', 'as', 'hide empty description', 'direction', 'TB', 'BT', 'RL', 'LR']
},
erDiagram: {
typeKeywords: ['erDiagram'],
blockKeywords: [],
keywords: ['title', 'accDescription']
},
journey: {
typeKeywords: ['journey'],
blockKeywords: ['section'],
keywords: ['title']
},
info: {
typeKeywords: ['info'],
blockKeywords: [],
keywords: ['showInfo']
},
gantt: {
typeKeywords: ['gantt'],
blockKeywords: [],
keywords: [
'title',
'dateFormat',
'axisFormat',
'todayMarker',
'section',
'excludes',
'inclusiveEndDates'
]
},
requirementDiagram: {
typeKeywords: ['requirement', 'requirementDiagram'],
blockKeywords: [...requirementDiagrams, 'element'],
keywords: []
},
gitGraph: {
typeKeywords: ['gitGraph'],
blockKeywords: [],
keywords: [
'accTitle',
'accDescr',
'commit',
'cherry-pick',
'branch',
'merge',
'reset',
'checkout',
'LR',
'BT',
'id',
'msg',
'type',
'tag',
'NORMAL',
'REVERSE',
'HIGHLIGHT'
]
},
pie: {
typeKeywords: ['pie'],
blockKeywords: [],
keywords: ['showData', 'title', 'accDescr', 'accTitle']
},
c4Diagram: {
typeKeywords: ['C4Context', 'C4Container', 'C4Component', 'C4Dynamic', 'C4Deployment'],
blockKeywords: [
'Boundary',
'Enterprise_Boundary',
'System_Boundary',
'Container_Boundary',
'Node',
'Node_L',
'Node_R'
],
keywords: [
'title',
'accDescription',
'direction',
'TB',
'BT',
'RL',
'LR',
'Person_Ext',
'Person',
'SystemQueue_Ext',
'SystemDb_Ext',
'System_Ext',
'SystemQueue',
'SystemDb',
'System',
'ContainerQueue_Ext',
'ContainerDb_Ext',
'Container_Ext',
'ContainerQueue',
'ContainerDb',
'Container',
'ComponentQueue_Ext',
'ComponentDb_Ext',
'Component_Ext',
'ComponentQueue',
'ComponentDb',
'Component',
'Deployment_Node',
'Rel',
'BiRel',
'Rel_Up',
'Rel_U',
'Rel_Down',
'Rel_D',
'Rel_Left',
'Rel_L',
'Rel_Right',
'Rel_R',
'Rel_Back',
'RelIndex'
]
},
sankey: {
typeKeywords: ['sankey-beta'],
blockKeywords: [],
keywords: []
}
};
const configDirectiveHandler = [
/^\s*%%(?={)/,
{
token: 'string',
next: '@configDirective',
nextEmbedded: 'javascript'
}
] as Monaco.languages.IShortMonarchLanguageRule1;
// Register a tokens provider for the mermaid language
monacoEditor.languages.setMonarchTokensProvider('mermaid', {
...Object.entries(keywords)
.map((entry) =>
Object.fromEntries(
Object.entries(entry[1]).map((deepEntry) => [
entry[0] + deepEntry[0][0].toUpperCase() + deepEntry[0].slice(1),
deepEntry[1]
])
)
)
// eslint-disable-next-line unicorn/no-array-reduce
.reduce(
(overallKeywords, nextKeyword) => ({
...overallKeywords,
...nextKeyword
}),
{}
),
tokenizer: {
root: [
[/^\s*gitGraph/m, 'typeKeyword', 'gitGraph'],
[/^\s*info/m, 'typeKeyword', 'info'],
[/^\s*pie/m, 'typeKeyword', 'pie'],
[/^\s*(flowchart|flowchart-v2|graph)/m, 'typeKeyword', 'flowchart'],
[/^\s*sequenceDiagram/, 'typeKeyword', 'sequenceDiagram'],
[/^\s*classDiagram(-v2)?/, 'typeKeyword', 'classDiagram'],
[/^\s*journey/, 'typeKeyword', 'journey'],
[/^\s*gantt/, 'typeKeyword', 'gantt'],
[/^\s*stateDiagram(-v2)?/, 'typeKeyword', 'stateDiagram'],
[/^\s*er(Diagram)?/, 'typeKeyword', 'erDiagram'],
[/^\s*requirement(Diagram)?/, 'typeKeyword', 'requirementDiagram'],
[/^\s*sankey-beta/m, 'typeKeyword', 'sankey'],
[
/^\s*(C4Context|C4Container|C4Component|C4Dynamic|C4Deployment)/m,
'typeKeyword',
'c4Diagram'
],
configDirectiveHandler,
[/%%[^${].*$/, 'comment']
],
configDirective: [[/%%$/, { token: 'string', next: '@pop', nextEmbedded: '@pop' }]],
gitGraph: [
configDirectiveHandler,
[/option(?=s)/, { token: 'typeKeyword', next: 'optionsGitGraph' }],
[/(accTitle|accDescr)(\s*:)(\s*[^\n\r]+$)/, ['keyword', 'delimiter.bracket', 'string']],
[
/(^\s*branch)(.*?)(\s+order)(:\s*)(\d+\s*$)/,
['keyword', 'variable', 'keyword', 'delimiter.bracket', 'number']
],
[/".*?"/, 'string'],
[
/(^\s*)(branch|reset|merge|checkout)(\s*\S+)/m,
['delimiter.bracket', 'keyword', 'variable']
],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@gitGraphBlockKeywords': 'typeKeyword',
'@gitGraphKeywords': 'keyword'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/\^/, 'delimiter.bracket']
],
optionsGitGraph: [
[
/s$/,
{
token: 'typeKeyword',
nextEmbedded: 'json'
}
],
['end', { token: 'typeKeyword', next: '@pop', nextEmbedded: '@pop' }]
],
info: [
[
/[A-Za-z][\w$]*/,
{
cases: {
'@infoBlockKeywords': 'typeKeyword',
'@infoKeywords': 'keyword'
}
}
]
],
pie: [
configDirectiveHandler,
[/(title|accDescription)(.*$)/, ['keyword', 'string']],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@pieBlockKeywords': 'typeKeyword',
'@pieKeywords': 'keyword'
}
}
],
[/".*?"/, 'string'],
[/\s*\d+/, 'number'],
[/:/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
flowchart: [
configDirectiveHandler,
[/[ox]?(--+|==+)[ox]/, 'transition'],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@flowchartBlockKeywords': 'typeKeyword',
'@flowchartKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/\|+.+?\|+/, 'string'],
[/\[+(\\.+?[/\\]|\/.+?[/\\])]+/, 'string'],
[/[>[]+[^[\]|]+?]+/, 'string'],
[/{+.+?}+/, 'string'],
[/\(+.+?\)+/, 'string'],
[/-\.+->?/, 'transition'],
[/(-[.-])([^>-][^-]+?)(-{3,}|-{2,}>|\.-+>)/, ['transition', 'string', 'transition']],
[/(==+)([^=]+?)(={3,}|={2,}>)/, ['transition', 'string', 'transition']],
[/<?(--+|==+)>|===+|---+/, 'transition'],
[/:::/, 'transition'],
[/[&;]/, 'delimiter.bracket'],
[/".*?"/, 'string'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
sequenceDiagram: [
configDirectiveHandler,
[/(title:?|accDescription)([^\n\r;]*$)/, ['keyword', 'string']],
[/(autonumber)([^\S\n\r]+off[^\S\n\r]*$)/, ['keyword', 'keyword']],
[/(autonumber)((?:[^\S\n\r]+\d+){2}[^\S\n\r]*$)/, ['keyword', 'number']],
[/(autonumber)([^\S\n\r]+\d+[^\S\n\r]*$)/, ['keyword', 'number']],
[
/(link\s+)(.*?)(:)(\s*.*?)(\s*@)(\s*[^\n\r;]+)/,
['keyword', 'variable', 'delimiter.bracket', 'string', 'delimiter.bracket', 'string']
],
[
/((?:links|properties)\s+)([^\n\r:]*?)(:\s+)/,
[
{ token: 'keyword' },
{ token: 'variable' },
{
token: 'delimiter.bracket',
nextEmbedded: 'javascript',
next: '@sequenceDiagramLinksProps'
}
]
],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@sequenceDiagramBlockKeywords': 'typeKeyword',
'@sequenceDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/(--?>?>|--?[)x])[+-]?/, 'transition'],
[/(:)([^\n:]*?$)/, ['delimiter.bracket', 'string']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
sequenceDiagramLinksProps: [
// [/^:/, { token: 'delimiter.bracket', nextEmbedded: 'json' }],
[/$|;/, { nextEmbedded: '@pop', next: '@pop', token: 'delimiter.bracket' }]
],
classDiagram: [
configDirectiveHandler,
[/(^\s*(?:title|accDescription))(\s+.*$)/, ['keyword', 'string']],
[
/(\*|<\|?|o|)(--|\.\.)(\*|\|?>|o|)([\t ]*[A-Za-z]+[\t ]*)(:)(.*?$)/,
['transition', 'transition', 'transition', 'variable', 'delimiter.bracket', 'string']
],
[/(?!class\s)([A-Za-z]+)(\s+[A-Za-z]+)/, ['type', 'variable']],
[/(\*|<\|?|o)?(--|\.\.)(\*|\|?>|o)?/, 'transition'],
[/^\s*class\s(?!.*{)/, 'keyword'],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@classDiagramBlockKeywords': 'typeKeyword',
'@classDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/(<<)(.+?)(>>)/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
[/".*?"/, 'string'],
[/:::/, 'transition'],
[/:|\+|-|#|~|\*\s*$|\$\s*$|\(|\)|{|}/, 'delimiter.bracket']
],
journey: [
configDirectiveHandler,
[/(title)(.*)/, ['keyword', 'string']],
[/(section)(.*)/, ['typeKeyword', 'string']],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@journeyBlockKeywords': 'typeKeyword',
'@journeyKeywords': 'keyword',
'@default': 'variable'
}
}
],
[
/(^\s*.+?)(:)(.*?)(:)(.*?)([$,])/,
[
'string',
'delimiter.bracket',
'number',
'delimiter.bracket',
'variable',
'delimiter.bracket'
]
],
[/,/, 'delimiter.bracket'],
[/(^\s*.+?)(:)([^:]*?)$/, ['string', 'delimiter.bracket', 'variable']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
gantt: [
configDirectiveHandler,
[/(title)(.*)/, ['keyword', 'string']],
[/(section)(.*)/, ['typeKeyword', 'string']],
[/^\s*([^\n:]*?)(:)/, ['string', 'delimiter.bracket']],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@ganttBlockKeywords': 'typeKeyword',
'@ganttKeywords': 'keyword'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/:/, 'delimiter.bracket']
],
stateDiagram: [
configDirectiveHandler,
[/note[^:]*$/, { token: 'typeKeyword', next: 'stateDiagramNote' }],
['hide empty description', 'keyword'],
[/^\s*state\s(?!.*{)/, 'keyword'],
[/(<<)(fork|join|choice)(>>)/, 'annotation'],
[/(\[\[)(fork|join|choice)(]])/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
[
/[A-Za-z][\w$]*/,
{
cases: {
'@stateDiagramBlockKeywords': 'typeKeyword',
'@stateDiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/".*?"/, 'string'],
[/(:)([^\n:]*?$)/, ['delimiter.bracket', 'string']],
[/{|}/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/-->/, 'transition'],
[/\[.*?]/, 'string']
],
stateDiagramNote: [
[/^\s*end note$/, { token: 'typeKeyword', next: '@pop' }],
[/.*/, 'string']
],
erDiagram: [
configDirectiveHandler,
[/(title|accDescription)(.*$)/, ['keyword', 'string']],
[/[|}][o|](--|\.\.)[o|][{|]/, 'transition'],
[/".*?"/, 'string'],
[/(:)(.*?$)/, ['delimiter.bracket', 'string']],
[/[:{}]/, 'delimiter.bracket'],
[/([A-Za-z]+)(\s+[A-Za-z]+)/, ['type', 'variable']],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/[A-Z_a-z-][\w$]*/, 'variable']
],
requirementDiagram: [
configDirectiveHandler,
[/->|<-|-/, 'transition'],
[/(\d+\.)*\d+/, 'number'],
[
/[A-Z_a-z-][\w$]*/,
{
cases: {
'@requirementDiagramBlockKeywords': 'typeKeyword',
'@default': 'variable'
}
}
],
[/[/:{}]/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
[/".*?"/, 'string']
],
c4Diagram: [
configDirectiveHandler,
[/(title|accDescription)(.*$)/, ['keyword', 'string']],
[/\(/, { token: 'delimiter.bracket', next: 'c4DiagramParenthesis' }],
[
/[A-Z_a-z-][\w$]*/,
{
cases: {
'@c4DiagramBlockKeywords': 'typeKeyword',
'@c4DiagramKeywords': 'keyword',
'@default': 'variable'
}
}
],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
],
c4DiagramParenthesis: [
[/,/, 'delimiter.bracket'],
[/\)/, { next: '@pop', token: 'delimiter.bracket' }],
[/[^),]/, 'string']
],
sankey: [
configDirectiveHandler,
[/(title)(.*)/, ['keyword', 'string']],
[/(accTitle|accDescr)(\s*:)(\s*[^\n\r]+$)/, ['keyword', 'delimiter.bracket', 'string']],
[/".*?"/, 'string'],
[/[A-Za-z]+/, 'string'],
[/\s*\d+/, 'number'],
[/,/, 'delimiter.bracket'],
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
]
}
});
monacoEditor.editor.defineTheme('mermaid-dark', {
base: 'vs-dark',
inherit: true,
colors: {},
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
{ token: 'identifier', foreground: '9cdcfe' }
]
});
monacoEditor.editor.defineTheme('mermaid', {
base: 'vs',
inherit: true,
colors: {},
rules: [
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
{ token: 'keyword', foreground: '649696' },
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
{ token: 'string', foreground: 'AA8500' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' },
{ token: 'annotation', foreground: '4b4b96' },
{ token: 'number', foreground: '4b4b96' },
{ token: 'comment', foreground: '888c89' },
{ token: 'variable', foreground: 'A22889' },
{ token: 'type', foreground: '2BDEA8' },
{ token: 'identifier', foreground: '9cdcfe' }
]
});
monacoEditor.languages.registerCompletionItemProvider('mermaid', {
provideCompletionItems: () => {
return {
suggestions: []
};
}
});
monacoEditor.languages.setLanguageConfiguration('mermaid', {
autoClosingPairs: [
{
open: '(',
close: ')'
},
{
open: '{',
close: '}'
},
{
open: '[',
close: ']'
}
],
brackets: [
['(', ')'],
['{', '}'],
['[', ']']
],
comments: {
lineComment: '%%'
}
});
};
+2 -2
View File
@@ -1,7 +1,7 @@
export const notify = (message: string): void => {
alert(message);
alert(message);
};
export const prompt = (message: string): boolean => {
return confirm(message);
return confirm(message);
};
+159 -167
View File
@@ -29,7 +29,7 @@ import type { Writable } from 'svelte/store';
* Disabled warnings about missing/unavailable storages
*/
export function disableWarnings(): void {
noWarnings = true;
noWarnings = true;
}
/**
* If set to true, no warning will be emitted if the requested Storage is not found.
@@ -39,110 +39,101 @@ let noWarnings = false;
/**
* List of storages where the warning have already been displayed.
*/
const alreadyWarnFor: Array<string> = [];
const alreadyWarnFor: string[] = [];
/**
* Add a log to indicate that the requested Storage have not been found.
* @param {string} storageName
*/
const warnStorageNotFound = (storageName) => {
const isProduction = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';
const warnStorageNotFound = (storageName: string) => {
const isProduction = typeof process !== 'undefined' && process.env.NODE_ENV === 'production';
if (!noWarnings && alreadyWarnFor.indexOf(storageName) === -1 && !isProduction) {
let message = `Unable to find the ${storageName}. No data will be persisted.`;
if (typeof window === 'undefined') {
message +=
'\n' +
'Are you running on a server? Most of storages are not available while running on a server.';
}
console.warn(message);
alreadyWarnFor.push(storageName);
}
};
const allowedClasses = [];
/**
* Add a class to the allowed list of classes to be serialized
* @param classDef The class to add to the list
*/
export const addSerializableClass = (classDef: () => unknown): void => {
allowedClasses.push(classDef);
if (!noWarnings && !alreadyWarnFor.includes(storageName) && !isProduction) {
let message = `Unable to find the ${storageName}. No data will be persisted.`;
if (typeof window === 'undefined') {
message +=
'\n' +
'Are you running on a server? Most of storages are not available while running on a server.';
}
console.warn(message);
alreadyWarnFor.push(storageName);
}
};
const serialize = (value: unknown): string => ESSerializer.serialize(value);
const deserialize = (value: string): unknown => {
// @TODO: to remove in the next major
if (value === 'undefined') {
return undefined;
}
const deserialize = (value?: string | null): unknown => {
// @TODO: to remove in the next major
if (value === 'undefined') {
return undefined;
}
if (value !== null && value !== undefined) {
try {
return ESSerializer.deserialize(value, allowedClasses);
} catch (e) {
// Do nothing
// use the value "as is"
}
try {
return JSON.parse(value);
} catch (e) {
// Do nothing
// use the value "as is"
}
}
return value;
if (value !== null && value !== undefined) {
try {
return ESSerializer.deserialize(value);
} catch {
// Do nothing
// use the value "as is"
}
try {
return JSON.parse(value);
} catch {
// Do nothing
// use the value "as is"
}
}
return value;
};
/**
* A store that keep it's value in time.
*/
export interface PersistentStore<T> extends Writable<T> {
/**
* Delete the store value from the persistent storage
*/
delete(): void;
/**
* Delete the store value from the persistent storage
*/
delete(): void;
}
/**
* Storage interface
*/
export interface StorageInterface<T> {
/**
* Get a value from the storage.
*
* If the value doesn't exists in the storage, `null` should be returned.
* This method MUST be synchronous.
* @param key The key/name of the value to retrieve
*/
getValue(key: string): T | null;
/**
* Get a value from the storage.
*
* If the value doesn't exists in the storage, `null` should be returned.
* This method MUST be synchronous.
* @param key The key/name of the value to retrieve
*/
getValue(key: string): T | null;
/**
* Save a value in the storage.
* @param key The key/name of the value to save
* @param value The value to save
*/
setValue(key: string, value: T): void;
/**
* Save a value in the storage.
* @param key The key/name of the value to save
* @param value The value to save
*/
setValue(key: string, value: T): void;
/**
* Remove a value from the storage
* @param key The key/name of the value to remove
*/
deleteValue(key: string): void;
/**
* Remove a value from the storage
* @param key The key/name of the value to remove
*/
deleteValue(key: string): void;
}
export interface SelfUpdateStorageInterface<T> extends StorageInterface<T> {
/**
* Add a listener to the storage values changes
* @param {string} key The key to listen
* @param {(newValue: T) => void} listener The listener callback function
*/
addListener(key: string, listener: (newValue: T) => void): void;
/**
* Remove a listener from the storage values changes
* @param {string} key The key that was listened
* @param {(newValue: T) => void} listener The listener callback function to remove
*/
removeListener(key: string, listener: (newValue: T) => void): void;
/**
* Add a listener to the storage values changes
* @param {string} key The key to listen
* @param {(newValue: T) => void} listener The listener callback function
*/
addListener(key: string, listener: (newValue: T) => void): void;
/**
* Remove a listener from the storage values changes
* @param {string} key The key that was listened
* @param {(newValue: T) => void} listener The listener callback function to remove
*/
removeListener(key: string, listener: (newValue: T) => void): void;
}
/**
@@ -152,114 +143,115 @@ export interface SelfUpdateStorageInterface<T> extends StorageInterface<T> {
* @param {string} key The name of the data key
*/
export function persist<T>(
store: Writable<T>,
storage: StorageInterface<T>,
key: string
store: Writable<T>,
storage: StorageInterface<T>,
key: string
): PersistentStore<T> {
const initialValue = storage.getValue(key);
const initialValue = storage.getValue(key);
if (null !== initialValue) {
store.set(initialValue);
}
if (null !== initialValue) {
store.set(initialValue);
}
if ((storage as SelfUpdateStorageInterface<T>).addListener) {
(storage as SelfUpdateStorageInterface<T>).addListener(key, (newValue) => {
store.set(newValue);
});
}
if ('addListener' in storage) {
(storage as SelfUpdateStorageInterface<T>).addListener(key, (newValue) => {
store.set(newValue);
});
}
store.subscribe((value) => {
storage.setValue(key, value);
});
store.subscribe((value) => {
storage.setValue(key, value);
});
return {
...store,
delete() {
storage.deleteValue(key);
}
};
return {
...store,
delete() {
storage.deleteValue(key);
}
};
}
function getBrowserStorage(
browserStorage: Storage,
listenExternalChanges = false
browserStorage: Storage,
listenExternalChanges = false
): SelfUpdateStorageInterface<any> {
const listeners: Array<{ key: string; listener: (newValue: any) => void }> = [];
const listenerFunction = (event: StorageEvent) => {
const eventKey = event.key;
if (event.storageArea === browserStorage) {
listeners
.filter(({ key }) => key === eventKey)
.forEach(({ listener }) => {
listener(deserialize(event.newValue));
});
}
};
const connect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.addEventListener) {
window.addEventListener('storage', listenerFunction);
}
};
const disconnect = () => {
if (listenExternalChanges && typeof window !== 'undefined' && window?.removeEventListener) {
window.removeEventListener('storage', listenerFunction);
}
};
const listeners: { key: string; listener: (newValue: any) => void }[] = [];
const listenerFunction = (event: StorageEvent) => {
const eventKey = event.key;
if (event.storageArea === browserStorage) {
for (const { listener } of listeners.filter(({ key }) => key === eventKey)) {
listener(deserialize(event.newValue));
}
}
};
const connect = () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.addEventListener) {
window.addEventListener('storage', listenerFunction);
}
};
const disconnect = () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (listenExternalChanges && typeof window !== 'undefined' && window.removeEventListener) {
window.removeEventListener('storage', listenerFunction);
}
};
return {
addListener(key: string, listener: (newValue: any) => void) {
listeners.push({ key, listener });
if (listeners.length === 1) {
connect();
}
},
removeListener(key: string, listener: (newValue: any) => void) {
const index = listeners.indexOf({ key, listener });
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
disconnect();
}
},
getValue(key: string): any | null {
const value = browserStorage.getItem(key);
return deserialize(value);
},
deleteValue(key: string) {
browserStorage.removeItem(key);
},
setValue(key: string, value: any) {
browserStorage.setItem(key, serialize(value));
}
};
return {
addListener(key: string, listener: (newValue: any) => void) {
listeners.push({ key, listener });
if (listeners.length === 1) {
connect();
}
},
removeListener(key: string, listener: (newValue: any) => void) {
const index = listeners.indexOf({ key, listener });
if (index !== -1) {
listeners.splice(index, 1);
}
if (listeners.length === 0) {
disconnect();
}
},
getValue(key: string): any {
const value = browserStorage.getItem(key);
return deserialize(value);
},
deleteValue(key: string) {
browserStorage.removeItem(key);
},
setValue(key: string, value: any) {
browserStorage.setItem(key, serialize(value));
}
};
}
/**
* Storage implementation that use the browser local storage
* @param {boolean} listenExternalChanges - Update the store if the localStorage is updated from another page
* @param listenExternalChanges - Update the store if the localStorage is updated from another page
*/
export function localStorage<T>(listenExternalChanges = false): StorageInterface<T> {
if (typeof window !== 'undefined' && window?.localStorage) {
return getBrowserStorage(window.localStorage, listenExternalChanges);
}
warnStorageNotFound('window.localStorage');
return noopStorage();
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (typeof window !== 'undefined' && window.localStorage) {
return getBrowserStorage(window.localStorage, listenExternalChanges);
}
warnStorageNotFound('window.localStorage');
return noopStorage();
}
/**
* Storage implementation that do nothing
*/
export function noopStorage(): StorageInterface<any> {
return {
getValue(): null {
return null;
},
deleteValue() {
// Do nothing
},
setValue() {
// Do nothing
}
};
export function noopStorage<T>(): StorageInterface<T> {
return {
getValue(): null {
return null;
},
deleteValue() {
// Do nothing
},
setValue() {
// Do nothing
}
};
}
+7
View File
@@ -0,0 +1,7 @@
<a
href="https://www.mermaidchart.com/app/user/billing/checkout?coupon=HOLIDAYS2023"
target="_blank"
class="flex-grow tracking-wide">
Get AI, team collaboration, storage, and more with
<span class="font-bold underline">Mermaid Chart Pro. Start free trial today & get 25% off.</span>
</a>
+25
View File
@@ -0,0 +1,25 @@
<script lang="ts">
const taglines = {
announcement_bar_ai_diagramming: 'Try diagramming with ChatGPT at Mermaid Chart',
announcement_bar_visual_editor: "Try Mermaid's Visual Editor at Mermaid Chart",
announcement_bar_live_collaboration: 'Enjoy live collaboration with teammates at Mermaid Chart'
};
const taglineKeys = Object.keys(taglines);
const taglineKey = taglineKeys[Math.floor(Math.random() * taglineKeys.length)];
const tagline = taglines[taglineKey];
const url =
'https://www.mermaidchart.com/?' +
new URLSearchParams({
utm_source: 'mermaid_live_editor',
utm_medium: taglineKey,
utm_campaign: 'promo_2024'
}).toString();
</script>
<a
href={url}
target="_blank"
class="flex flex-grow justify-center gap-6 align-middle tracking-wide">
{tagline}
<button class="rounded bg-gray-800 p-1 px-4 text-sm font-light">Try it now</button>
</a>
+54
View File
@@ -0,0 +1,54 @@
import { writable, type Writable, get } from 'svelte/store';
import { persist, localStorage } from '../persist';
import Holiday2023 from './Holiday2023.svelte';
import Jan2024 from './Jan2024.svelte';
interface Promotion {
id: string;
startDate: Date;
endDate: Date;
component: ConstructorOfATypedSvelteComponent;
}
const promotions: Promotion[] = [
{
id: 'holiday-2023',
startDate: new Date('2023-11-27'),
endDate: new Date('2024-01-09'),
component: Holiday2023
},
{
id: 'jan-2024',
startDate: new Date('2024-01-10'),
endDate: new Date('2024-06-01'),
component: Jan2024
}
];
export const dismissPromotion = (id?: string): void => {
if (!id) {
return;
}
dismissedPromotionsStore.update((dismissedIDs: string[]) => {
dismissedIDs.push(id);
return dismissedIDs;
});
};
const dismissedPromotionsStore: Writable<string[]> = persist(
writable([]),
localStorage(),
'dismissedPromotions'
);
export const getActivePromotion = (): Promotion | undefined => {
const dismissedPromotions = get(dismissedPromotionsStore);
const now = new Date();
return promotions
.filter(
(p: Promotion) =>
p.startDate <= now && p.endDate >= now && !dismissedPromotions.includes(p.id)
)
.sort((a: Promotion, b: Promotion) => b.endDate.getTime() - a.endDate.getTime())
.pop();
};
+30 -30
View File
@@ -3,38 +3,38 @@ import { serializeState, deserializeState, type SerdeType } from './serde';
import { defaultState } from './state';
import type { State } from '$lib/types';
const verifySerde = (state: State, serde?: SerdeType): string => {
const serialized = serializeState(state, serde);
const deserialized = deserializeState(serialized);
expect(deserialized).to.deep.equal(state);
return serialized;
};
describe('Serde tests', () => {
const verifySerde = (state: State, serde?: SerdeType): string => {
const serialized = serializeState(state, serde);
const deserialized = deserializeState(serialized);
expect(deserialized).to.deep.equal(state);
return serialized;
};
it('should serialize and deserialize with default serde', () => {
expect(verifySerde(defaultState)).toMatchInlineSnapshot(
`"pako:eNpVjs1qw0AMhF9F6NRC_AI-FBq7zSXQQnPz5iBs2bvE-8NaSwi2373r-NLqJM18M2jG1neMJfajv7eaosClVg7yvDeVjmYSS9MViuJtObGA9Y4fCxxfTh4m7UMwbnjd-eMGQTWfN4xBtHG3dbeqZ_7L8QJ1c6YgPlz_Ope7X-CjMd861_93dOSc-mx6KnsqWopQUXwieEDL0ZLp8vvzpigUzZYVlnntuKc0ikLl1oxSEv_zcC2WEhMfMPo0aMyd45SvFDoSrg0NkeyOrL_WfFuF"`
);
});
it('should serialize and deserialize with default serde', () => {
expect(verifySerde(defaultState)).toMatchInlineSnapshot(
'"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
);
});
it('should serialize and deserialize with base64 serde', () => {
expect(verifySerde(defaultState, 'base64')).toMatchInlineSnapshot(
`"base64:eyJjb2RlIjoiZmxvd2NoYXJ0IFREXG4gICAgQVtDaHJpc3RtYXNdIC0tPnxHZXQgbW9uZXl8IEIoR28gc2hvcHBpbmcpXG4gICAgQiAtLT4gQ3tMZXQgbWUgdGhpbmt9XG4gICAgQyAtLT58T25lfCBEW0xhcHRvcF1cbiAgICBDIC0tPnxUd298IEVbaVBob25lXVxuICAgIEMgLS0-fFRocmVlfCBGW2ZhOmZhLWNhciBDYXJdXG4gICIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJhdXRvU3luYyI6dHJ1ZSwicm91Z2giOmZhbHNlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"`
);
});
it('should serialize and deserialize with base64 serde', () => {
expect(verifySerde(defaultState, 'base64')).toMatchInlineSnapshot(
'"base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl1cbiAgIiwibWVybWFpZCI6IntcbiAgXCJ0aGVtZVwiOiBcImRlZmF1bHRcIlxufSIsImF1dG9TeW5jIjp0cnVlLCJ1cGRhdGVEaWFncmFtIjp0cnVlfQ"'
);
});
it('should serialize and deserialize with pako serde', () => {
expect(verifySerde(defaultState, 'pako')).toMatchInlineSnapshot(
`"pako:eNpVjs1qw0AMhF9F6NRC_AI-FBq7zSXQQnPz5iBs2bvE-8NaSwi2373r-NLqJM18M2jG1neMJfajv7eaosClVg7yvDeVjmYSS9MViuJtObGA9Y4fCxxfTh4m7UMwbnjd-eMGQTWfN4xBtHG3dbeqZ_7L8QJ1c6YgPlz_Ope7X-CjMd861_93dOSc-mx6KnsqWopQUXwieEDL0ZLp8vvzpigUzZYVlnntuKc0ikLl1oxSEv_zcC2WEhMfMPo0aMyd45SvFDoSrg0NkeyOrL_WfFuF"`
);
});
it('should serialize and deserialize with pako serde', () => {
expect(verifySerde(defaultState, 'pako')).toMatchInlineSnapshot(
'"pako:eNpVj81qw0AMhF9F6NRC_AI-BGK7zSXQQHLz5iBsObuk-8Naphjb7551fEl1EjPfiNGEjW8Zc7xHChqulXKQ5lCXOppeLPU3yLL9fGQB6x2PMxQfRw-99iEYd__c-GKFoJxOK8Yg2rjHslnlK__jeIaqPlEQH27vzvXPz_BVm7NO5_87OnJKfdcd5R1lDUUoKb4Q3KHlaMm0qfq0KgpFs2WFeVpb7mj4FYXKLQmlQfxldA3mEgfe4RBaEq4MpaftJi5PNtJU8w"'
);
});
it('should throw error for unrecognized serde', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
expect(() => serializeState(defaultState, 'unknown')).toThrowError(
'Unknown serde type: unknown'
);
expect(() => deserializeState('unknown:hello')).toThrowError('Unknown serde type: unknown');
});
it('should throw error for unrecognized serde', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
expect(() => serializeState(defaultState, 'unknown')).toThrowError(
'Unknown serde type: unknown'
);
expect(() => deserializeState('unknown:hello')).toThrowError('Unknown serde type: unknown');
});
});
+40 -40
View File
@@ -3,61 +3,61 @@ import { toUint8Array, fromUint8Array, toBase64, fromBase64 } from 'js-base64';
import type { State } from '$lib/types';
interface Serde {
serialize: (state: string) => string;
deserialize: (state: string) => string;
serialize: (state: string) => string;
deserialize: (state: string) => string;
}
const base64Serde: Serde = {
serialize: (state: string): string => {
return toBase64(state, true);
},
deserialize: (state: string): string => {
return fromBase64(state);
}
serialize: (state: string): string => {
return toBase64(state, true);
},
deserialize: (state: string): string => {
return fromBase64(state);
}
};
export const pakoSerde: Serde = {
serialize: (state: string): string => {
const data = new TextEncoder().encode(state);
const compressed = deflate(data, { level: 9 });
return fromUint8Array(compressed, true);
},
deserialize: (state: string): string => {
const data = toUint8Array(state);
return inflate(data, { to: 'string' });
}
serialize: (state: string): string => {
const data = new TextEncoder().encode(state);
const compressed = deflate(data, { level: 9 });
return fromUint8Array(compressed, true);
},
deserialize: (state: string): string => {
const data = toUint8Array(state);
return inflate(data, { to: 'string' });
}
};
export type SerdeType = 'base64' | 'pako';
const serdes: { [key in SerdeType]: Serde } = {
base64: base64Serde,
pako: pakoSerde
base64: base64Serde,
pako: pakoSerde
};
export const serializeState = (state: State, serde: SerdeType = 'pako'): string => {
if (serdes[serde] === undefined) {
throw new Error(`Unknown serde type: ${serde}`);
}
const json = JSON.stringify(state);
const serialized = serdes[serde].serialize(json);
return `${serde}:${serialized}`;
if (!(serde in serdes)) {
throw new Error(`Unknown serde type: ${serde}`);
}
const json = JSON.stringify(state);
const serialized = serdes[serde].serialize(json);
return `${serde}:${serialized}`;
};
export const deserializeState = (state: string): State => {
let type: SerdeType, serialized: string;
if (state.includes(':')) {
let tempType: string;
[tempType, serialized] = state.split(':');
if (tempType in serdes) {
type = tempType as SerdeType;
} else {
throw new Error(`Unknown serde type: ${tempType}`);
}
} else {
type = 'base64';
serialized = state;
}
const json = serdes[type].deserialize(serialized);
return JSON.parse(json) as State;
let type: SerdeType, serialized: string;
if (state.includes(':')) {
let tempType: string;
[tempType, serialized] = state.split(':');
if (tempType in serdes) {
type = tempType as SerdeType;
} else {
throw new Error(`Unknown serde type: ${tempType}`);
}
} else {
type = 'base64';
serialized = state;
}
const json = serdes[type].deserialize(serialized);
return JSON.parse(json) as State;
};
+135 -141
View File
@@ -1,35 +1,29 @@
import { writable, get, derived } from 'svelte/store';
import { persist, localStorage } from './persist';
import { saveStatistics, countLines } from './stats';
import { serializeState, deserializeState } from './serde';
import { cmdKey } from './util';
import { parse, init } from './mermaid';
import type { Readable } from 'svelte/store';
import type { MarkerData, State, ValidatedState } from '$lib/types';
void init();
import type { ErrorHash, MarkerData, State, ValidatedState } from '$lib/types';
import { debounce } from 'lodash-es';
import type { MermaidConfig } from 'mermaid';
import { derived, get, writable, type Readable } from 'svelte/store';
import { parse } from './mermaid';
import { localStorage, persist } from './persist';
import { deserializeState, serializeState } from './serde';
import { errorDebug, formatJSON } from './util';
export const defaultState: State = {
code: `graph TD
code: `flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
mermaid: JSON.stringify(
{
theme: 'default'
},
null,
2
),
autoSync: true,
updateDiagram: true
mermaid: formatJSON({
theme: 'default'
}),
autoSync: true,
rough: false,
updateDiagram: true
};
const urlParseFailedState = `graph TD
const urlParseFailedState = `flowchart TD
A[Loading URL failed. We can try to figure out why.] -->|Decode JSON| B(Please check the console to see the JSON and error details.)
B --> C{Is the JSON correct?}
C -->|Yes| D(Please Click here to Raise an issue in github.<br/>Including the broken link in the issue <br/> will speed up the fix.)
@@ -43,149 +37,149 @@ const urlParseFailedState = `graph TD
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
export const currentState: ValidatedState = (() => {
const state = get(inputStateStore);
return {
...state,
serialized: serializeState(state),
errorMarkers: [],
error: undefined,
editorMode: state.editorMode ?? 'code'
};
})();
const processState = async (state: State) => {
const processed: ValidatedState = {
...state,
serialized: '',
errorMarkers: [],
error: undefined,
editorMode: state.editorMode ?? 'code'
};
// No changes should be done to fields part of `state`.
try {
processed.serialized = serializeState(state);
await parse(state.code);
JSON.parse(state.mermaid);
} catch (error) {
processed.error = error as Error;
errorDebug();
console.error(error);
if ('hash' in error) {
try {
const {
loc: { first_line, last_line, first_column, last_column }
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
} = error.hash as ErrorHash;
const marker: MarkerData = {
severity: 8, // Error
startLineNumber: first_line,
startColumn: first_column,
endLineNumber: last_line,
endColumn: last_column + 1,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
message: error.str
};
processed.errorMarkers = [marker];
} catch (error) {
console.error('Error without line helper', error);
}
}
}
return processed;
};
// All internal reads should be done via stateStore, but it should not be persisted/shared externally.
export const stateStore: Readable<ValidatedState> = derived(
[inputStateStore],
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
async ([state], set) => {
const processed: ValidatedState = {
...state,
serialized: '',
errorMarkers: [],
error: undefined,
editorMode: state.editorMode ?? 'code'
};
// No changes should be done to fields part of `state`.
try {
processed.serialized = serializeState(state);
await parse(state.code);
JSON.parse(state.mermaid);
} catch (e) {
processed.error = e;
console.error(e);
if (e.hash) {
try {
const marker: MarkerData = {
severity: 8, // Error
startLineNumber: e.hash.loc.first_line,
startColumn: e.hash.loc.first_column,
endLineNumber: e.hash.loc.last_line,
endColumn: (e.hash.loc.last_column as number) + 1,
message: e.str
};
processed.errorMarkers = [marker];
} catch (err) {
console.error('Error without line helper', err);
}
}
}
set(processed);
},
{
...get(inputStateStore),
serialized: '',
errorMarkers: [],
error: undefined,
editorMode: get(inputStateStore).editorMode ?? 'code'
}
[inputStateStore],
([state], set) => {
void processState(state).then(set);
},
currentState
);
export const loadState = (data: string): void => {
let state: State;
console.log(`Loading '${data}'`);
try {
state = deserializeState(data);
const mermaidConfig: { [key: string]: string } =
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
if (
mermaidConfig.securityLevel &&
mermaidConfig.securityLevel !== 'strict' &&
confirm(
`Removing "securityLevel":"${mermaidConfig.securityLevel}" from the config for safety.\nClick Cancel if you trust the source of this Diagram.`
)
) {
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
}
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
} catch (e) {
state = get(inputStateStore);
if (data) {
console.error('Init error', e);
state.code = urlParseFailedState;
state.mermaid = defaultState.mermaid;
}
}
updateCodeStore({ ...state });
let state: State;
console.log(`Loading '${data}'`);
try {
state = deserializeState(data);
const mermaidConfig: MermaidConfig =
typeof state.mermaid === 'string'
? (JSON.parse(state.mermaid) as MermaidConfig)
: state.mermaid;
if (
mermaidConfig.securityLevel &&
mermaidConfig.securityLevel !== 'strict' &&
confirm(
`Removing "securityLevel":"${mermaidConfig.securityLevel}" from the config for safety.\nClick Cancel if you trust the source of this Diagram.`
)
) {
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
}
state.mermaid = formatJSON(mermaidConfig);
} catch (error) {
state = get(inputStateStore);
if (data) {
console.error('Init error', error);
state.code = urlParseFailedState;
state.mermaid = defaultState.mermaid;
}
}
updateCodeStore(state);
};
export const updateCodeStore = (newState: Partial<State>): void => {
inputStateStore.update((state) => {
// console.log({ newState, state });
return { ...state, ...newState };
});
inputStateStore.update((state) => {
return { ...state, ...newState };
});
};
let prompted = false;
export const updateCode = (
code: string,
{
updateDiagram = false,
resetPanZoom = false
}: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
code: string,
{
updateDiagram = false,
resetPanZoom = false
}: { updateDiagram?: boolean; resetPanZoom?: boolean } = {}
): void => {
const lines = countLines(code);
saveStatistics(code);
errorDebug();
if (lines > 50 && !prompted && get(stateStore).autoSync) {
const turnOff = confirm(
`Long diagram detected. Turn off Auto Sync? Use ${cmdKey} + Enter or click the sync logo to manually sync.`
);
prompted = true;
if (turnOff) {
updateCodeStore({
autoSync: false
});
}
}
inputStateStore.update((state) => {
if (resetPanZoom) {
state.pan = undefined;
state.zoom = undefined;
}
return { ...state, code, updateDiagram };
});
inputStateStore.update((state) => {
if (resetPanZoom) {
state.pan = undefined;
state.zoom = undefined;
}
return { ...state, code, updateDiagram };
});
};
export const updateConfig = (config: string): void => {
inputStateStore.update((state) => {
return { ...state, mermaid: config };
});
// console.log('updateConfig', config);
inputStateStore.update((state) => {
return { ...state, mermaid: config };
});
};
export const toggleDarkTheme = (dark: boolean): void => {
inputStateStore.update((state) => {
const config = JSON.parse(state.mermaid);
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
config.theme = dark ? 'dark' : 'default';
}
inputStateStore.update((state) => {
const config = JSON.parse(state.mermaid) as MermaidConfig;
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
config.theme = dark ? 'dark' : 'default';
}
return { ...state, mermaid: JSON.stringify(config, null, 2) };
});
return { ...state, mermaid: formatJSON(config) };
});
};
let urlDebounce: number;
export const initURLSubscription = (): void => {
stateStore.subscribe(({ serialized }) => {
clearTimeout(urlDebounce);
urlDebounce = window.setTimeout(() => {
history.replaceState(undefined, undefined, `#${serialized}`);
}, 250);
});
const updateHash = debounce((hash) => {
history.replaceState(undefined, '', `#${hash}`);
}, 250);
stateStore.subscribe(({ serialized }) => {
updateHash(serialized);
});
};
export const getStateString = (): string => {
return JSON.stringify(get(inputStateStore));
return JSON.stringify(get(inputStateStore));
};
+10 -10
View File
@@ -1,19 +1,19 @@
import { describe, it, expect } from 'vitest';
import { detectType } from './stats';
describe('diagram detection', () => {
it('should detect diagrams correctly', () => {
expect(
detectType(`%%{{
it('should detect diagrams correctly', () => {
expect(
detectType(`%%{{
graph`)
).toBe('graph');
expect(detectType(`gitGraph`)).toBe('gitGraph');
expect(
detectType(`%%{{
).toBe('graph');
expect(detectType(`gitGraph`)).toBe('gitGraph');
expect(
detectType(`%%{{
flowChart
graph`)
).toBe('flowChart');
expect(detectType(`loki -> thor`)).toBe(undefined);
});
).toBe('flowChart');
expect(detectType(`loki -> thor`)).toBe(undefined);
});
});
+122 -77
View File
@@ -1,98 +1,143 @@
import { browser } from '$app/environment';
import type { AnalyticsInstance } from 'analytics';
export let analytics: AnalyticsInstance;
import type PlausibleInstance from 'plausible-tracker';
import { env } from './env';
export let plausible: ReturnType<typeof PlausibleInstance> | undefined;
export const initAnalytics = async (): Promise<void> => {
if (browser && !analytics) {
try {
const [{ Analytics }, { default: plausible }] = await Promise.all([
import('analytics'),
import('analytics-plugin-plausible')
]);
analytics = Analytics({
app: 'mermaid-live-editor',
plugins: [
plausible({
domain: 'mermaid.live',
hashMode: false,
trackLocalhost: false, // By default 'false'
apiHost: 'https://plausible.io'
})
]
});
} catch (e) {
console.log(e);
console.info('Analytics blocked ;)');
}
}
if (browser && !plausible) {
try {
const { default: Plausible } = await import('plausible-tracker');
plausible = Plausible({
domain: env.domain,
hashMode: false,
// All tracked stats are public and available at https://p.mermaid.live/mermaid.live
apiHost: env.analyticsUrl
});
} catch (error) {
console.log(error);
console.info('Analytics blocked ;)');
}
}
};
export const detectType = (text: string): string => {
const possibleDiagramTypes = [
'classDiagram',
'erDiagram',
'flowChart',
'gantt',
'gitGraph',
'graph',
'journey',
'pie',
'stateDiagram'
];
const firstLine = text
.replace(/^\s*%%.*\n/g, '\n')
.trimStart()
.split(' ')[0]
.toLowerCase();
const detectedDiagram = possibleDiagramTypes.find((d) => firstLine.includes(d.toLowerCase()));
return detectedDiagram;
export const detectType = (text: string): string | undefined => {
const possibleDiagramTypes = [
'classDiagram',
'erDiagram',
'flowChart',
'gantt',
'gitGraph',
'graph',
'journey',
'pie',
'stateDiagram',
'quadrantChart',
'mindmap'
];
const firstLine = text
.replaceAll(/^\s*%%.*\n/g, '\n')
.trimStart()
.split(' ')[0]
.toLowerCase();
const detectedDiagram = possibleDiagramTypes.find((d) => firstLine.includes(d.toLowerCase()));
return detectedDiagram;
};
export const countLines = (code: string): number => {
return (code.match(/\n/g) || '').length + 1;
return (code.match(/\n/g)?.length ?? 0) + 1;
};
export const saveStatistics = (graph: string): void => {
const graphType = detectType(graph);
if (!graphType) {
return;
}
const length = countLines(graph);
logEvent('render', { graphType, length });
export const saveStatistics = ({
code,
renderTime,
isRough
}: {
code: string;
renderTime: number;
isRough: boolean;
}): void => {
const graphType = detectType(code);
if (!graphType) {
return;
}
const length = countLines(code);
const lengthBucket = getBucket(length);
const renderTimeMsBucket = getBucket(renderTime);
logEvent('render', { graphType, length, lengthBucket, renderTimeMsBucket, isRough });
};
const getBucket = (length: number): string => {
return length < 10
? '0-10'
: length < 25
? '10-25'
: length < 50
? '25-50'
: length < 100
? '50-100'
: length < 200
? '100-200'
: length < 500
? '200-500'
: length < 700
? '500-700'
: length < 1000
? '700-1000'
: length < 1500
? '1000-1500'
: length < 2500
? '1500-2500'
: length < 4500
? '2500-4500'
: length < 7000
? '4500-7000'
: length < 10_000
? '7000-10000'
: '10000+';
};
const minutesToMilliSeconds = (minutes: number): number => {
return minutes * 60_000;
return minutes * 60_000;
};
const defaultDelay = minutesToMilliSeconds(1);
const delaysPerEvent = {
render: minutesToMilliSeconds(5),
panZoom: minutesToMilliSeconds(10),
copyClipboard: defaultDelay,
download: defaultDelay,
copyMarkdown: defaultDelay,
loadGist: defaultDelay,
loadSampleDiagram: defaultDelay,
renderDiagram: defaultDelay,
history: defaultDelay,
migration: defaultDelay,
themeChange: defaultDelay
render: minutesToMilliSeconds(5),
panZoom: minutesToMilliSeconds(10),
copyClipboard: defaultDelay,
download: defaultDelay,
copyMarkdown: defaultDelay,
loadGist: defaultDelay,
loadSampleDiagram: defaultDelay,
renderDiagram: defaultDelay,
history: defaultDelay,
migration: defaultDelay,
themeChange: defaultDelay,
bannerClick: defaultDelay,
version: defaultDelay
};
export type AnalyticsEvent = keyof typeof delaysPerEvent;
const timeouts: Record<string, number> = {};
const timeouts: Map<string, number> = new Map<string, number>();
// manual debounce to reduce the number of events sent to analytics
export const logEvent = (name: AnalyticsEvent, data?: unknown): void => {
if (!analytics) {
return;
}
const key = data ? JSON.stringify({ name, data }) : name;
if (timeouts[key] === undefined) {
void analytics.track(name, data);
} else {
clearTimeout(timeouts[key]);
}
timeouts[key] = window.setTimeout(() => {
delete timeouts[key];
}, delaysPerEvent[name]);
export const logEvent = (
name: AnalyticsEvent,
data?: Record<string, string | number | boolean>
): void => {
if (!plausible) {
return;
}
const key = data ? JSON.stringify({ name, data }) : name;
if (timeouts.has(key)) {
clearTimeout(timeouts.get(key));
} else {
plausible.trackEvent(
name,
{ props: data },
{ url: window.location.origin + window.location.pathname }
);
}
timeouts.set(
key,
window.setTimeout(() => timeouts.delete(key), delaysPerEvent[name])
);
};
+24 -24
View File
@@ -4,35 +4,35 @@ import { persist, localStorage } from '$lib/util/persist';
import { logEvent } from './stats';
export interface ThemeConfig {
isDark: boolean;
theme?: string;
isDark: boolean;
theme?: string;
}
export const themeStore: Writable<ThemeConfig> = persist(
writable({
isDark: false
}),
localStorage(),
'themeStore'
writable({
isDark: false
}),
localStorage(),
'themeStore'
);
const darkThemes = [
'dark',
'synthwave',
'halloween',
'aqua',
'forest',
'luxury',
'black',
'dracula'
];
const darkThemes = new Set([
'dark',
'synthwave',
'halloween',
'aqua',
'forest',
'luxury',
'black',
'dracula'
]);
export const setTheme = (theme: string): void => {
if (theme.includes(' ')) {
theme = theme.split(' ')[1].trim();
}
const isDark = darkThemes.includes(theme);
console.log('Setting theme', theme);
themeStore.set({ theme, isDark });
logEvent('themeChange', { theme, isDark });
if (theme.includes(' ')) {
theme = theme.split(' ')[1].trim();
}
const isDark = darkThemes.has(theme);
console.log('Setting theme', theme);
themeStore.set({ theme, isDark });
logEvent('themeChange', { theme, isDark });
};
+33 -13
View File
@@ -1,28 +1,48 @@
import { initURLSubscription, loadState, updateCodeStore } from './state';
import { analytics, initAnalytics } from './stats';
import { plausible, initAnalytics } from './stats';
import { loadDataFromUrl } from './fileLoaders/loader';
import { initLoading } from './loading';
import { applyMigrations } from './migrations';
export const loadStateFromURL = (): void => {
loadState(window.location.hash.slice(1));
loadState(window.location.hash.slice(1));
};
export const syncDiagram = (): void => {
updateCodeStore({
updateDiagram: true
});
updateCodeStore({
updateDiagram: true
});
};
export const initHandler = async (): Promise<void> => {
applyMigrations();
loadStateFromURL();
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
syncDiagram();
initURLSubscription();
await initAnalytics();
analytics?.page();
applyMigrations();
loadStateFromURL();
await initLoading('Loading Gist...', loadDataFromUrl().catch(console.error));
syncDiagram();
initURLSubscription();
await initAnalytics();
plausible?.trackPageview({ url: window.location.origin + window.location.pathname });
};
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
export const isMac = navigator.platform.toUpperCase().includes('MAC');
export const cmdKey = isMac ? 'Cmd' : 'Ctrl';
let count = 0;
export const errorDebug = (limit = 1000) => {
count += 1;
if (count > limit) {
console.log(count, limit);
// eslint-disable-next-line no-debugger
debugger;
}
};
export const formatJSON = (data: unknown): string => JSON.stringify(data, undefined, 2);
export const fetchJSON = async <T>(url: string): Promise<T> => {
const res = await fetch(url);
return res.json() as T;
};
export const fetchText = async (url: string): Promise<string> => {
const res = await fetch(url);
return res.text();
};
+1
View File
@@ -1,3 +1,4 @@
export const prerender = true;
export const csr = true;
export const ssr = false;
export const trailingSlash = 'ignore';
+70 -70
View File
@@ -1,87 +1,87 @@
<script lang="ts">
import '../app.postcss';
import { base } from '$app/paths';
import { onMount } from 'svelte';
import { loadingStateStore } from '$lib/util/loading';
import { setTheme, themeStore } from '$lib/util/theme';
import { toggleDarkTheme } from '$lib/util/state';
import { initHandler } from '$lib/util/util';
import '../app.postcss';
import { base } from '$app/paths';
import { onMount } from 'svelte';
import { loadingStateStore } from '$lib/util/loading';
import { setTheme, themeStore } from '$lib/util/theme';
import { toggleDarkTheme } from '$lib/util/state';
import { initHandler } from '$lib/util/util';
// 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(() => {
window.addEventListener('hashchange', async (ev) => {
await initHandler();
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register(`${base}/service-worker.js`, {
scope: `${base}/`
})
.then(function (registration) {
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function (error) {
console.log('Service worker registration failed, error:', error);
});
}
// 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(() => {
window.addEventListener('hashchange', () => {
void initHandler();
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register(`${base}/service-worker.js`, {
scope: `${base}/`
})
.then(function (registration) {
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function (error) {
console.log('Service worker registration failed, error:', error);
});
}
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
if ($themeStore.theme === undefined) {
setTheme(isDarkMode ? 'dark' : 'light');
}
if ($themeStore.theme === undefined) {
setTheme(isDarkMode ? 'dark' : 'light');
}
themeStore.subscribe(({ theme, isDark }) => {
if (theme) {
document.getElementsByTagName('html')[0].setAttribute('data-theme', theme);
toggleDarkTheme(isDark);
}
});
});
themeStore.subscribe(({ theme, isDark }) => {
if (theme) {
document.querySelectorAll('html')[0].dataset.theme = theme;
toggleDarkTheme(isDark);
}
});
});
</script>
<main class="h-screen text-primary-content">
<slot />
<slot />
</main>
{#if $loadingStateStore.loading}
<div
class="w-screen h-screen z-50 absolute left-0 top-0 bg-gray-600 opacity-50 flex align-middle justify-center">
<div class="text-indigo-100 text-4xl font-bold my-auto">
<div class="loader mx-auto" />
<div>{$loadingStateStore.message}</div>
</div>
</div>
<div
class="absolute left-0 top-0 z-50 flex h-screen w-screen justify-center bg-gray-600 align-middle opacity-50">
<div class="my-auto text-4xl font-bold text-indigo-100">
<div class="loader mx-auto" />
<div>{$loadingStateStore.message}</div>
</div>
</div>
{/if}
<style>
.loader {
border: 0.45em solid #f3f3f3;
border-radius: 50%;
border-top: 0.45em solid #6365f1;
width: 3em;
height: 3em;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
.loader {
border: 0.45em solid #f3f3f3;
border-radius: 50%;
border-top: 0.45em solid #6365f1;
width: 3em;
height: 3em;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
/* Safari */
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
+14 -14
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { base } from '$app/paths';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { base } from '$app/paths';
onMount(async () => {
// Handle old live editor links and redirect to new version
const hash = window.location.hash.split('/');
let newURL = 'edit';
if (hash.length > 2) {
newURL = `${hash[1]}#${hash[2]}`;
}
await goto(`${base}/${newURL}`, {
replaceState: true
});
});
onMount(async () => {
// Handle old live editor links and redirect to new version
const hash = window.location.hash.split('/');
let newURL = 'edit';
if (hash.length > 2) {
newURL = `${hash[1]}#${hash[2]}`;
}
await goto(`${base}/${newURL}`, {
replaceState: true
});
});
</script>
+241 -180
View File
@@ -1,199 +1,260 @@
<script lang="ts">
import Editor from '$lib/components/editor.svelte';
import Navbar from '$lib/components/navbar.svelte';
import Preset from '$lib/components/preset.svelte';
import Actions from '$lib/components/actions.svelte';
import View from '$lib/components/view.svelte';
import Card from '$lib/components/card/card.svelte';
import History from '$lib/components/history/history.svelte';
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { cmdKey, initHandler, syncDiagram } from '$lib/util/util';
import { onMount } from 'svelte';
import type { Tab, DocConfig, EditorMode, ValidatedState } from '$lib/types';
import { base } from '$app/paths';
import { dev } from '$app/environment';
import { base } from '$app/paths';
import Actions from '$lib/components/Actions.svelte';
import Card from '$lib/components/Card/Card.svelte';
import Editor from '$lib/components/Editor.svelte';
import History from '$lib/components/History/History.svelte';
import Navbar from '$lib/components/Navbar.svelte';
import Preset from '$lib/components/Preset.svelte';
import View from '$lib/components/View.svelte';
import type { DocumentationConfig, EditorMode, Tab, ValidatedState } from '$lib/types';
import { inputStateStore, stateStore, updateCodeStore } from '$lib/util/state';
import { cmdKey, initHandler, syncDiagram } from '$lib/util/util';
import { onMount } from 'svelte';
const docURLBase = 'https://mermaid-js.github.io/mermaid';
const docMap: DocConfig = {
graph: {
code: '/#/flowchart',
config: '/#/flowchart?id=configuration'
},
flowchart: {
code: '/#/flowchart',
config: '/#/flowchart?id=configuration'
},
sequenceDiagram: {
code: '/#/sequenceDiagram',
config: '/#/sequenceDiagram?id=configuration'
},
classDiagram: {
code: '/#/classDiagram',
config: '/#/classDiagram?id=configuration'
},
'stateDiagram-v2': {
code: '/#/stateDiagram'
},
gantt: {
code: '/#/gantt',
config: '/#/gantt?id=configuration'
},
pie: {
code: '/#/pie'
},
erDiagram: {
code: '/#/entityRelationshipDiagram',
config: '/#/entityRelationshipDiagram?id=styling'
},
journey: {
code: '/#/user-journey'
},
gitGraph: {
code: '/#/gitgraph',
config: '/#/gitgraph?id=gitgraph-specific-configuration-options'
}
};
let docURL = docURLBase;
let activeTabID = 'code';
stateStore.subscribe(({ code, editorMode }: ValidatedState) => {
activeTabID = editorMode;
const codeTypeMatch = /([\S]+)[\s\n]/.exec(code);
if (codeTypeMatch && codeTypeMatch.length > 1) {
const docKey = codeTypeMatch[1];
const docConfig = docMap[docKey] ?? { code: '' };
docURL = docURLBase + (docConfig[editorMode] ?? docConfig.code ?? '');
}
});
const MCBaseURL = dev ? 'http://localhost:5174' : 'https://mermaidchart.com';
const docURLBase = 'https://mermaid.js.org';
const docMap: DocumentationConfig = {
graph: {
code: '/syntax/.html',
config: '/syntax/.html#configuration'
},
flowchart: {
code: '/syntax/flowchart.html',
config: '/syntax/flowchart.html#configuration'
},
sequenceDiagram: {
code: '/syntax/sequenceDiagram.html',
config: '/syntax/sequenceDiagram.html#configuration'
},
classDiagram: {
code: '/syntax/classDiagram.html',
config: '/syntax/classDiagram.html#configuration'
},
'stateDiagram-v2': {
code: '/syntax/stateDiagram.html'
},
gantt: {
code: '/syntax/gantt.html',
config: '/syntax/gantt.html#configuration'
},
pie: {
code: '/syntax/pie.html',
config: '/syntax/pie.html#configuration'
},
erDiagram: {
code: '/syntax/entityRelationshipDiagram.html',
config: '/syntax/entityRelationshipDiagram.html#styling'
},
journey: {
code: '/syntax/userJourney.html'
},
gitGraph: {
code: '/syntax/gitgraph.html',
config: '/syntax/gitgraph.html#gitgraph-specific-configuration-options'
},
quadrantChart: {
code: '/syntax/quadrantChart.html',
config: '/syntax/quadrantChart.html#chart-configurations'
},
requirementDiagram: {
code: '/syntax/requirementDiagram.html'
},
C4Context: {
code: '/syntax/c4.html'
},
mindmap: {
code: '/syntax/mindmap.html'
},
timeline: {
code: '/syntax/timeline.html',
config: '/syntax/timeline.html#themes'
},
zenuml: {
code: '/syntax/zenuml.html'
},
'sankey-beta': {
code: '/syntax/sankey.html',
config: '/syntax/sankey.html#configuration'
},
'xychart-beta': {
code: '/syntax/xyChart.html',
config: '/syntax/xyChart.html#chart-configurations'
}
};
let docURL = docURLBase;
let activeTabID = 'code';
let docKey = '';
stateStore.subscribe(({ code, editorMode }: ValidatedState) => {
activeTabID = editorMode;
const codeTypeMatch = /(\S+)\s/.exec(code);
if (codeTypeMatch && codeTypeMatch.length > 1) {
docKey = codeTypeMatch[1];
const docConfig = docMap[docKey] ?? { code: '' };
docURL = docURLBase + (docConfig[editorMode] ?? docConfig.code ?? '');
}
});
const tabSelectHandler = (message: CustomEvent<Tab>) => {
const editorMode: EditorMode = message.detail.id === 'code' ? 'code' : 'config';
updateCodeStore({ editorMode });
};
const tabSelectHandler = (message: CustomEvent<Tab>) => {
const editorMode: EditorMode = message.detail.id === 'code' ? 'code' : 'config';
updateCodeStore({ editorMode });
};
const tabs: Tab[] = [
{
id: 'code',
title: 'Code',
icon: 'fas fa-code'
},
{
id: 'config',
title: 'Config',
icon: 'fas fa-cogs'
}
];
const tabs: Tab[] = [
{
id: 'code',
title: 'Code',
icon: 'fas fa-code'
},
{
id: 'config',
title: 'Config',
icon: 'fas fa-cogs'
}
];
onMount(async () => {
await initHandler();
const resizer = document.getElementById('resizeHandler');
const element = document.getElementById('editorPane');
const resize = (e: { pageX: number }) => {
const newWidth = e.pageX - element.getBoundingClientRect().left;
if (newWidth > 50) {
element.style.width = `${newWidth}px`;
}
};
onMount(async () => {
await initHandler();
const resizer = document.querySelector<HTMLElement>('#resizeHandler');
const element = document.querySelector<HTMLElement>('#editorPane');
if (!resizer || !element) {
console.debug('Failed to find resize handler or editor pane', { resizer, element });
return;
}
const resize = ({ pageX }: { pageX: number }) => {
const newWidth = pageX - element.getBoundingClientRect().left;
if (newWidth > 50) {
element.style.width = `${newWidth}px`;
}
};
const stopResize = () => {
window.removeEventListener('mousemove', resize);
};
resizer.addEventListener('mousedown', (e) => {
e.preventDefault();
window.addEventListener('mousemove', resize);
window.addEventListener('mouseup', stopResize);
});
});
const stopResize = () => {
window.removeEventListener('mousemove', resize);
};
resizer.addEventListener('mousedown', (event) => {
event.preventDefault();
window.addEventListener('mousemove', resize);
window.addEventListener('mouseup', stopResize);
});
});
</script>
<div class="h-full flex flex-col overflow-hidden">
<Navbar />
<div class="flex-1 flex overflow-hidden">
<div class="hidden md:flex flex-col" id="editorPane" style="width: 40%">
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} {activeTabID} title="Mermaid">
<div slot="actions" class="flex flex-row items-center">
<div class="form-control flex-row items-center">
<label class="cursor-pointer label" for="autoSync">
<span> Auto sync</span>
<input
type="checkbox"
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
id="autoSync"
bind:checked={$inputStateStore.autoSync} />
</label>
</div>
<div class="flex h-full flex-col overflow-hidden">
<Navbar />
<div class="flex flex-1 overflow-hidden">
<div class="hidden flex-col md:flex" id="editorPane" style="width: 40%">
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} {activeTabID} title="Mermaid">
<div slot="actions" class="flex flex-row items-center">
<div class="form-control flex-row items-center">
<label class="label cursor-pointer" for="autoSync">
<span> Auto sync</span>
<input
type="checkbox"
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
id="autoSync"
bind:checked={$inputStateStore.autoSync} />
</label>
</div>
{#if !$stateStore.autoSync}
<button
class="btn btn-secondary btn-xs mr-1"
title="Sync Diagram ({cmdKey} + Enter)"
data-cy="sync"
on:click={syncDiagram}><i class="fas fa-sync" /></button>
{/if}
{#if !$stateStore.autoSync}
<button
class="btn btn-secondary btn-xs mr-1"
title="Sync Diagram ({cmdKey} + Enter)"
data-cy="sync"
on:click={syncDiagram}><i class="fas fa-sync" /></button>
{/if}
<button class="btn btn-secondary btn-xs" title="View documentation">
<a target="_blank" href={docURL} data-cy="docs"><i class="fas fa-book mr-1" />Docs</a>
</button>
</div>
<button
class="btn btn-secondary btn-xs"
title="View documentation for {docKey.replace('Diagram', '')} diagram">
<a target="_blank" href={docURL} data-cy="docs">
<i class="fas fa-book mr-1" />Docs
</a>
</button>
</div>
<Editor />
</Card>
<Editor />
</Card>
<div class="-mt-2">
<Preset />
<History />
<Actions />
</div>
</div>
<div id="resizeHandler" class="hidden md:block" />
<div class="flex-1 flex flex-col overflow-hidden">
<Card title="Diagram" isCloseable={false}>
<div slot="actions" class="flex flex-row items-center">
<label class="cursor-pointer label py-0" for="panZoom">
<span>Pan & Zoom</span>
<input
type="checkbox"
class="toggle {$stateStore.panZoom ? 'btn-secondary' : 'toggle-primary'} ml-1"
id="panZoom"
bind:checked={$inputStateStore.panZoom} />
</label>
<a
href={`${base}/view#${$stateStore.serialized}`}
target="_blank"
class="btn btn-secondary btn-xs"
title="View diagram in new page"
><i class="fas fa-external-link-alt mr-1" />Full screen</a>
</div>
<div class="-mt-2">
<Preset />
<History />
<Actions />
</div>
</div>
<div id="resizeHandler" class="hidden md:block" />
<div class="flex flex-1 flex-col overflow-hidden">
<Card title="Diagram" isCloseable={false}>
<div slot="actions" class="flex flex-row items-center gap-2">
<label
class="label flex cursor-pointer gap-1 py-0"
title="Rough mode is in beta. Features like clickable nodes, Pan & Zoom, will be disabled."
for="rough">
<span>Rough</span>
<input
type="checkbox"
class="toggle {$stateStore.rough ? 'btn-secondary' : 'toggle-primary'}"
id="rough"
bind:checked={$inputStateStore.rough} />
</label>
<label
class="label flex cursor-pointer gap-1 py-0"
title={$stateStore.rough ? 'Pan & Zoom is disabled in rough mode.' : ''}
for="panZoom">
<span>Pan & Zoom</span>
<input
type="checkbox"
class="toggle {$stateStore.panZoom ? 'btn-secondary' : 'toggle-primary'}"
id="panZoom"
disabled={$stateStore.rough}
bind:checked={$inputStateStore.panZoom} />
</label>
<a
href={`${base}/view#${$stateStore.serialized}`}
target="_blank"
class="btn btn-secondary btn-xs gap-1"
title="View diagram in new page"><i class="fas fa-external-link-alt" />Full screen</a>
<a
href={`${MCBaseURL}/app/plugin/save?state=${$stateStore.serialized}`}
target="_blank"
class="btn btn-secondary btn-xs gap-1 bg-[#FF3570]"
title="Save diagram in Mermaid Chart"
><img src="./mermaidchart-logo.svg" class="h-5 w-5" alt="Mermaid chart logo" />Save to
Mermaid Chart</a>
</div>
<div class="flex-1 overflow-auto">
<View />
</div>
</Card>
<div class="md:hidden rounded shadow p-2 mx-2">
Code editing not supported on mobile. Please use a desktop browser.
</div>
</div>
</div>
<div class="flex-1 overflow-auto">
<View />
</div>
</Card>
<div class="mx-2 rounded p-2 shadow md:hidden">
Code editing not supported on mobile. Please use a desktop browser.
</div>
</div>
</div>
</div>
<style>
#resizeHandler {
cursor: col-resize;
padding: 0 2px;
}
#resizeHandler {
cursor: col-resize;
padding: 0 2px;
}
#resizeHandler::after {
width: 2px;
height: 100%;
top: 0;
content: '';
position: absolute;
background-color: hsla(var(--b3));
margin-left: -1px;
transition-duration: 0.2s;
}
#resizeHandler::after {
width: 2px;
height: 100%;
top: 0;
content: '';
position: absolute;
background-color: hsla(var(--b3));
margin-left: -1px;
transition-duration: 0.2s;
}
#resizeHandler:hover::after {
margin-left: -2px;
background-color: hsla(var(--p));
width: 4px;
}
#resizeHandler:hover::after {
margin-left: -2px;
background-color: hsla(var(--p));
width: 4px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More