Compare commits

..
Author SHA1 Message Date
Sidharth Vinod eca8781445 temp 2025-05-26 22:49:02 +05:30
Sidharth Vinod 9626f6b5df fix: PNG export icons 2025-05-09 21:58:33 +05:30
Sidharth VinodandGitHub 5ce27dd981 Merge pull request #1714 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency lucide-svelte to ^0.507.0
2025-05-06 05:13:58 +00:00
Sidharth VinodandGitHub 91fc0895b2 Merge pull request #1712 from mermaid-js/update-announcement-bar
chore: Update announcement bar
2025-05-06 10:43:47 +05:30
Sidharth VinodandGitHub 48c23374a4 Merge pull request #1713 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2025-05-06 10:43:31 +05:30
renovate[bot]andGitHub 829ee28cf9 fix(deps): update all non-major dependencies 2025-05-06 01:45:13 +00:00
rajat-ht 39876254ca Update as per PR suggestion 2025-05-05 23:50:20 +05:30
Sidharth Vinod 0b5a038c9d chore: Cleanup tagline logic 2025-05-05 21:40:43 +05:30
renovate[bot]andGitHub cc702bb556 chore(deps): update dependency lucide-svelte to ^0.507.0 2025-05-05 01:48:03 +00:00
rajat-ht c9e61440f4 chore: Update announcement bar 2025-05-02 18:18:52 +05:30
Sidharth VinodandGitHub 881648a87e Merge pull request #1703 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-05-02 00:58:57 +05:30
renovate[bot]andGitHub d3c8efd712 chore(deps): update all non-major dependencies 2025-05-01 16:01:56 +00:00
Sidharth VinodandGitHub 9777e1782f Merge pull request #1702 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2025-05-01 15:59:58 +00:00
renovate[bot]andGitHub 9391b17366 fix(deps): update all non-major dependencies 2025-05-01 13:15:09 +00:00
Sidharth VinodandGitHub 6b42be2841 Merge pull request #1710 from mermaid-js/sidv/1659
fix: #1659 Frozen UI with comment regex
2025-05-01 18:43:11 +05:30
Sidharth Vinod 6a07387688 fix: #1659 Frozen UI with comment regex 2025-05-01 17:57:40 +05:30
Sidharth VinodandGitHub 4350d7e13a Merge pull request #1700 from mermaid-js/sidv/jsonSchema
feat: Support jsonSchema to validate config
2025-04-19 13:52:47 +00:00
Sidharth Vinod 2eaf136c75 feat: Support jsonSchema to validate config 2025-04-19 12:11:59 +05:30
Sidharth VinodandGitHub 5c35d5e589 Merge pull request #1698 from jsoref/docs/link
chore: Improve screen reader link for `live version`
2025-04-18 12:25:05 -07:00
Josh Soref e90ef3af9e link: live version
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-04-18 08:39:00 -04:00
Sidharth Vinod 0479af715b 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 dependency vite to v5.4.18 [security]
  chore(deps): update all non-major dependencies
2025-04-16 16:26:21 +05:30
Sidharth Vinod 8aafb909a6 fix: Fallback page 2025-04-16 16:26:15 +05:30
Sidharth VinodandGitHub c9f6816fb6 Merge pull request #1685 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v5.4.18 [security]
2025-04-16 10:47:30 +00:00
renovate[bot]andGitHub e1e7552462 chore(deps): update dependency vite to v5.4.18 [security] 2025-04-16 10:47:13 +00:00
Sidharth VinodandGitHub 64787974d8 Merge pull request #1690 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-04-16 03:46:18 -07:00
Sidharth Vinod 1052420e34 fix: Fallback to sveltekit on 404 in GH pages 2025-04-16 16:14:47 +05:30
renovate[bot]andGitHub a54971bc56 chore(deps): update all non-major dependencies 2025-04-16 10:12:03 +00:00
Sidharth VinodandGitHub f27f4b336c Merge pull request #1691 from mermaid-js/renovate/npm-sveltejs-kit-vulnerability
chore(deps): update dependency @sveltejs/kit to v2.20.6 [security]
2025-04-16 03:10:35 -07:00
Sidharth Vinod bf25d3cc60 fix: New diagram link 2025-04-16 15:39:57 +05:30
renovate[bot]andGitHub 87af8a8c30 chore(deps): update dependency @sveltejs/kit to v2.20.6 [security] 2025-04-14 19:30:27 +00:00
Sidharth VinodandGitHub df6c1cceee Merge pull request #1688 from mermaid-js/sidv/faLoad
feat: Wait for fontawesome to load before rendering
2025-04-14 14:31:59 +00:00
Sidharth VinodandGitHub 987e09d8bc Merge pull request #1680 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-04-13 12:41:21 +00:00
Sidharth VinodandAlois Klink 6ae68fc546 feat: Wait for fontawesome to load
Co-authored-by: Alois Klink <alois@aloisklink.com>
2025-04-13 18:03:59 +05:30
renovate[bot]andGitHub 22444a2000 chore(deps): update all non-major dependencies 2025-04-13 08:27:39 +00:00
Sidharth VinodandGitHub 92f029830f Merge pull request #1687 from mermaid-js/sidv/mobile
feat: Add mobile support
2025-04-13 08:25:59 +00:00
Sidharth Vinod 8b13bf48e7 feat: Support pan while zooming 2025-04-13 13:50:30 +05:30
285baa4c83 Update src/lib/util/panZoom.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-13 01:16:56 -07:00
Sidharth Vinod 7c22576783 chore: Track PWA install device type 2025-04-13 13:41:26 +05:30
Sidharth Vinod e11017672f chore: Track PWA installs 2025-04-13 13:40:50 +05:30
Sidharth Vinod f4a736db62 chore: Add analytics for mobileViewToggle 2025-04-13 13:38:02 +05:30
Sidharth Vinod 2b242befe1 fix: Reduce gap in mobile 2025-04-13 13:34:51 +05:30
Sidharth VinodandAlois the eagle Klink 3039d5cb47 fix: SVG size to avoid
Co-authored-by: Alois "the eagle" Klink <alois@aloisklink.com>
2025-04-13 13:16:44 +05:30
Sidharth Vinod 4bf6dc0db6 chore: Unify code update logic 2025-04-13 13:12:30 +05:30
Sidharth Vinod 795663aafa chore: Cleanup deps 2025-04-13 12:01:15 +05:30
Sidharth Vinod bd1ca7e2ab feat: Add syntax highlighting 2025-04-13 11:57:53 +05:30
Sidharth Vinod 89f648ab8f chore: Update theme-color 2025-04-13 10:13:38 +05:30
Sidharth Vinod ff1a0d97d3 chore: Update og:image 2025-04-13 10:13:11 +05:30
Sidharth Vinod a11aae3573 chore: Update theme color 2025-04-13 10:02:11 +05:30
Sidharth Vinod 6c8402948a feat: Support touch pan & zoom 2025-04-13 09:51:39 +05:30
Sidharth Vinod a5df32931c fix: Responsive breakpoints, remove zoom icons in mobile 2025-04-13 09:51:22 +05:30
Sidharth Vinod 835231d291 fix: Promo bar alignment 2025-04-13 09:50:35 +05:30
Sidharth Vinod 4c31e1fe5d fix: Orientation in PWA manifest 2025-04-13 09:50:13 +05:30
Sidharth VinodandAlois Klink ebff808b31 fix: Privacy popup size
Co-authored-by: Alois Klink <alois@aloisklink.com>
2025-04-13 01:31:31 +05:30
Sidharth Vinod 224296c1d8 fix: padding 2025-04-13 01:27:38 +05:30
Sidharth Vinod 4ec52d9282 chore: Simplify pane logic 2025-04-13 01:25:18 +05:30
Sidharth Vinod 74ad9f44d0 feat: Add mobile support 2025-04-12 20:00:52 +05:30
Sidharth VinodandGitHub 96dddeb34d Merge pull request #1681 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-04-12 09:07:24 +00:00
renovate[bot]andGitHub 6528a0a26a chore(deps): update all non-major dependencies 2025-04-11 22:52:35 +00:00
Sidharth Vinod c530984c97 chore: Simplify URL params setting 2025-04-03 23:24:54 +05:30
Sidharth VinodandGitHub 643a7ca760 Merge pull request #1676 from mermaid-js/sidv/consolidateUTMSource
fix: Consolidate UTM source
2025-04-03 10:49:37 -07:00
Sidharth Vinod aa0d62ba11 fix: Consolidate UTM source 2025-04-03 23:16:20 +05:30
Sidharth VinodandGitHub 110b95d854 Merge pull request #1674 from mermaid-js/promo-links-v2
Update promo bar urls
2025-04-03 17:03:28 +00:00
Steph db0e97b988 update promo bar urls 2025-04-03 08:59:14 -07:00
Sidharth VinodandGitHub a2965bc3ca Merge pull request #1672 from mermaid-js/trackConversion
MC-2931 Add UTM params to MC URLs
2025-04-02 12:21:31 -07:00
Sidharth Vinod a24c66b554 Merge branch 'develop' into trackConversion
* develop:
  fix URLS
2025-04-02 23:54:51 +05:30
Sidharth Vinod b17e136225 MC-2931 Add utm params to MC URLs 2025-04-02 23:50:12 +05:30
Sidharth VinodandGitHub 3be0ad92f5 Merge pull request #1670 from mermaid-js/update-promo-links
Promo bars - fix URLS
2025-04-02 10:56:07 -07:00
Steph a35b1afa41 fix URLS 2025-04-02 08:59:02 -07:00
Sidharth VinodandGitHub 4d1a0ae07b Merge pull request #1667 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v5.4.16 [security]
2025-04-02 02:31:12 -07:00
renovate[bot]andGitHub b7b8d02dd0 chore(deps): update dependency vite to v5.4.16 [security] 2025-04-02 09:30:26 +00:00
Sidharth VinodandGitHub 12b0a27cf8 Merge pull request #1653 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-04-02 09:29:16 +00:00
renovate[bot]andGitHub cd5a858dcc chore(deps): update all non-major dependencies 2025-04-02 09:23:00 +00:00
Sidharth VinodandGitHub 034fccaebb Merge pull request #1663 from mermaid-js/renovate/mermaid-packages
fix(deps): update dependency mermaid to ^11.6.0
2025-04-02 02:20:34 -07:00
Sidharth VinodandGitHub 8fd44b8d5b Merge pull request #1652 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-04-02 02:20:17 -07:00
Sidharth VinodandGitHub a5c4036927 Merge pull request #1665 from mermaid-js/announcement-bar
Update announcement bar
2025-04-02 02:19:48 -07:00
Sidharth Vinod 72e8e583a9 chore: Update fade delay 2025-04-02 14:46:59 +05:30
renovate[bot]andGitHub 5befc1221c fix(deps): update dependency mermaid to ^11.6.0 2025-04-02 09:15:27 +00:00
renovate[bot]andGitHub bc57d8660e chore(deps): update all non-major dependencies 2025-04-02 09:15:01 +00:00
Sidharth VinodandGitHub 5aa0d4a921 Merge branch 'develop' into announcement-bar 2025-04-02 02:14:03 -07:00
Sidharth Vinod 8320bb0651 fix: Type issue on lint 2025-04-02 14:43:45 +05:30
Sidharth VinodandGitHub 50180ed72a Merge branch 'develop' into announcement-bar 2025-04-02 01:58:16 -07:00
Sidharth Vinod 1f702e4960 fix: Update download image size to match CI 2025-04-02 14:24:58 +05:30
Steph af4ef4865b update announcement bar 2025-04-01 23:29:33 -07:00
Sidharth Vinod 37716245f7 fix #1655: Reset pan & zoom state if invalid 2025-03-26 09:36:28 -07:00
Sidharth VinodandGitHub 097f6f0f34 Merge pull request #1633 from mermaid-js/sidv/redesign
Redesign
2025-03-24 10:40:44 -07:00
Sidharth Vinod 0eda1c0b8b fix: Temp hack for zenuml 2025-03-24 10:34:42 -07:00
Sidharth Vinod 3124112006 fix: OptimizeDeps 2025-03-20 12:29:12 -07:00
Sidharth Vinod ee446eb2b8 fix: Dark mode code icon 2025-03-20 12:21:26 -07:00
Sidharth Vinod 866ca5f747 chore: Tweak popup text 2025-03-20 12:17:15 -07:00
Sidharth Vinod 41019ffa9a chore: Minor design tweaks 2025-03-20 12:07:28 -07:00
Sidharth Vinod 64e156085b test: Fix tests 2025-03-19 19:54:42 -07:00
Sidharth Vinod 501c983b99 test: Fix tests 2025-03-19 19:10:51 -07:00
Sidharth Vinod 3005deffc5 chore: Fix lint error 2025-03-18 13:00:08 -07:00
Sidharth Vinod f166156b86 Merge branch 'develop' into sidv/redesign
* develop:
  chore: Update script name
  chore: Remove cypress
  chore: Ignore cypress
  chore: Update test exclude
  chore: Tweak test config
  chore: Ignore cypress
  chore: Fix pw issues
  chore: Add playwright tests
2025-03-18 12:59:32 -07:00
Sidharth Vinod 9250ac3eaa feat: Change Switch to Button for grid and rough 2025-03-18 12:58:25 -07:00
Sidharth Vinod 6111a40dc3 chore: Center empty history text 2025-03-18 12:57:50 -07:00
Sidharth VinodandGitHub 93d02aadc0 Merge pull request #1649 from mermaid-js/sidv/pw
chore: Add playwright tests
2025-03-18 12:57:23 -07:00
Sidharth Vinod c97c6c2199 chore: Fix icon path 2025-03-18 11:55:05 -07:00
Sidharth Vinod b3093087f6 chore: Fix icon path 2025-03-18 11:54:09 -07:00
Sidharth Vinod d29ad6345b fix: Update MC Icon 2025-03-18 11:51:19 -07:00
Sidharth Vinod 7c725556b0 chore: Tweak render delay 2025-03-18 11:42:49 -07:00
Sidharth Vinod df26e5176f chore: Fix spacing in main menu 2025-03-18 11:16:30 -07:00
Sidharth Vinod 3b2c7caf07 fix: New diagram button, add MC links 2025-03-18 11:11:31 -07:00
Sidharth Vinod d039f6534a feat: Disable mermaidchart on unsupported diagrams 2025-03-18 10:51:08 -07:00
Sidharth Vinod 4d08026b77 fix: Add collapse icon to card 2025-03-18 10:50:37 -07:00
Sidharth Vinod 9d1df6126b chore: Fix timeline height 2025-03-18 10:12:11 -07:00
Sidharth Vinod ac308717f9 feat: Update theme icon animation 2025-03-17 23:45:14 -07:00
Sidharth Vinod 4c0de566f0 chore: Update script name 2025-03-17 22:16:30 -07:00
Sidharth Vinod 324dd0b4b2 chore: Remove cypress 2025-03-17 21:27:33 -07:00
Sidharth Vinod 5ed07028c7 chore: Ignore cypress 2025-03-17 20:04:15 -07:00
Sidharth Vinod 9f06808026 chore: Update test exclude 2025-03-17 20:03:07 -07:00
Sidharth Vinod 23c93e9e92 chore: Tweak test config 2025-03-17 19:59:34 -07:00
Sidharth Vinod 65faee55b7 chore: Ignore cypress 2025-03-17 19:51:38 -07:00
Sidharth Vinod 87e0b91543 chore: Fix pw issues 2025-03-17 19:51:04 -07:00
Sidharth Vinod bb084867fa chore: Add playwright tests 2025-03-17 19:47:13 -07:00
Sidharth Vinod b7da3f80f7 chore: Remove testing-library
It wasn't used for any relevant tests, and svelte5 support is spotty.
2025-03-17 18:15:23 -07:00
Sidharth Vinod 893f8625d5 chore: Update snapshots 2025-03-17 13:30:36 -07:00
Sidharth Vinod 355f1929b2 chore: Sort classes 2025-03-17 13:27:44 -07:00
Sidharth VinodandSteph 059a1a98ab Update docs button target
Co-authored-by: Steph <35910788+huynhicode@users.noreply.github.com>
2025-03-17 13:04:47 -07:00
Sidharth Vinod ea0fc8a621 Merge branch 'develop' into sidv/redesign
* develop:
  chore(deps): update all non-major dependencies
  fix(deps): update all non-major dependencies
2025-03-17 12:47:24 -07:00
Sidharth VinodandGitHub 631cc43f2b Merge pull request #1647 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-03-17 12:44:07 -07:00
Sidharth Vinod d0b2cf49da chore: Update imports, cleanup classes 2025-03-17 12:43:32 -07:00
Sidharth Vinod bbb1fd2dae chore: Update security verbiage 2025-03-17 12:42:38 -07:00
Sidharth Vinod 176698a299 chore: Set MC Popup to bottom 2025-03-17 12:42:04 -07:00
Sidharth Vinod 4dbe52126e chore: Cleanup editor 2025-03-17 12:41:40 -07:00
Sidharth Vinod 3fd8c43212 chore: Unify copy button 2025-03-17 12:41:18 -07:00
renovate[bot]andGitHub 75c4764da8 chore(deps): update all non-major dependencies 2025-03-17 19:10:34 +00:00
Sidharth VinodandGitHub afd5aaaada Merge pull request #1646 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2025-03-17 12:09:32 -07:00
Sidharth Vinod cd6f1ca613 fix: Reduce sync threshold, increase wait time 2025-03-17 11:19:05 -07:00
Sidharth Vinod 8da3cdedd9 fix: Use McWrapper, fix PNG export 2025-03-17 11:18:40 -07:00
Sidharth Vinod a72e13bb5c fix: FA icons 2025-03-17 10:16:05 -07:00
Sidharth Vinod 9ddc37add5 chore: Fix png export background 2025-03-17 09:39:51 -07:00
Sidharth Vinod cb267911f5 chore: Fix primary dark mode 2025-03-17 09:30:14 -07:00
renovate[bot]andGitHub d6eb0533c6 fix(deps): update all non-major dependencies 2025-03-17 16:23:21 +00:00
Sidharth Vinod b76fa8106e Merge branch 'develop' into sidv/redesign
* develop:
  chore: Update deps
  chore(deps): update all non-major dependencies
  chore: Prettier ignore pnpm-lock
  chore: Update build dependencies
  chore: Update actions
  chore: Update node version
  chore: Switch from yarn to pnpm
  fix(deps): update dependency mermaid to v11.5.0
2025-03-17 09:21:04 -07:00
Sidharth VinodandGitHub 4a8dbfadba Merge pull request #1644 from mermaid-js/sidv/pnpm
chore: Switch from yarn to pnpm
2025-03-16 19:49:53 -07:00
Sidharth Vinod 15df6a2fcb chore: Update deps 2025-03-16 19:47:10 -07:00
Sidharth Vinod 506128c5df Merge branch 'develop' into sidv/pnpm
* develop:
  chore(deps): update all non-major dependencies
  fix(deps): update dependency mermaid to v11.5.0
2025-03-16 19:45:42 -07:00
Sidharth VinodandGitHub 1195ea94b3 Merge pull request #1642 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-03-16 19:43:06 -07:00
renovate[bot]andGitHub 90894781cd chore(deps): update all non-major dependencies 2025-03-17 02:42:56 +00:00
Sidharth VinodandGitHub 18cd3c4591 Merge pull request #1643 from mermaid-js/renovate/mermaid-packages
fix(deps): update dependency mermaid to v11.5.0
2025-03-16 19:42:04 -07:00
Sidharth Vinod 4091da60c6 chore: Prettier ignore pnpm-lock 2025-03-16 19:41:19 -07:00
Sidharth Vinod 4a143ae8e2 chore: Update build dependencies 2025-03-16 19:40:25 -07:00
Sidharth Vinod 2159375e81 chore: Update actions 2025-03-16 19:37:05 -07:00
Sidharth Vinod 5742878799 chore: Update node version 2025-03-16 19:33:45 -07:00
Sidharth Vinod 592c3addac chore: Switch from yarn to pnpm 2025-03-16 19:19:10 -07:00
renovate[bot]andGitHub 9beb411c14 fix(deps): update dependency mermaid to v11.5.0 2025-03-17 00:35:39 +00:00
Sidharth Vinod 21be4caf82 Merge branch 'develop' into sidv/redesign
* develop:
  chore: Fix coverage dependency
  chore: sort gitignore
  chore: Add redirect to home on 404 error
  chore: Update subdomain
  chore: Update import
  chore: Remove redundant config
  chore: Add CNAME
  chore: Remove lint after build
  chore: Use official action to deploy to pages
  build: update Browserslist db
  fix: use `<output>` and `<pre>` to format errors
  Revert "feat: Sanitize error message"
2025-03-16 17:34:39 -07:00
Sidharth Vinod 6e7c7467a3 fix: PanZoom 2025-03-16 17:32:14 -07:00
Sidharth VinodandGitHub b5be8ca4fc Merge pull request #1639 from mermaid-js/browserslist-update
build: update Browserslist db
2025-03-15 20:35:47 -07:00
Sidharth Vinod 66b4a427b7 chore: Fix coverage dependency 2025-03-15 20:35:03 -07:00
Sidharth Vinod 3a65f2382d chore: sort gitignore 2025-03-15 20:31:15 -07:00
Sidharth Vinod c677afc04a chore: Add redirect to home on 404 error 2025-03-15 20:30:41 -07:00
Sidharth Vinod 4973083133 chore: Update subdomain 2025-03-15 20:09:21 -07:00
Sidharth Vinod 9288094c30 chore: Update import 2025-03-15 20:00:13 -07:00
Sidharth Vinod fe428c7cea chore: Remove redundant config 2025-03-15 19:55:50 -07:00
Sidharth Vinod 423fe7e0d6 chore: Add CNAME 2025-03-15 19:49:00 -07:00
Sidharth Vinod c9905606a1 chore: Remove lint after build 2025-03-15 19:32:37 -07:00
Sidharth VinodandGitHub e0cbcd21a8 Merge pull request #1637 from mermaid-js/release-promotion
Release live editor
2025-03-15 19:29:41 -07:00
Sidharth Vinod 214f6d0862 chore: Use official action to deploy to pages 2025-03-15 19:25:47 -07:00
GitHub Action 80526e58f7 build: update Browserslist db 2025-03-15 02:45:22 +00:00
Sidharth Vinod b2bb5b2bef chore: Minor cleanup 2025-03-14 19:37:25 -07:00
Sidharth Vinod c9efb9e017 fix: body style 2025-03-14 19:06:21 -07:00
Sidharth Vinod 34b078e7e8 fix: Grid color 2025-03-14 19:06:06 -07:00
Sidharth VinodandGitHub e9bc0c24de Merge pull request #1496 from aloisklink/fix/stop-parsing-syntax-error-as-html
Use `<output>` and `<pre>` to format mermaid errors
2025-03-15 07:30:21 +05:30
Sidharth Vinod bfc7535d54 chore: Remove outOfSync 2025-03-14 18:50:25 -07:00
Sidharth Vinod dcdc19d15e chore: Simplify class def 2025-03-14 18:16:44 -07:00
Sidharth Vinod a9db7e0513 Merge branch 'develop' into sidv/redesign
* develop:
  fix: Flickering when rendering, PanZoom & Rough mode combined
  chore: Simplify queueing logic
  fix: Blank screen on initial load
2025-03-14 18:14:36 -07:00
Sidharth VinodandGitHub d92c1e5e4c Merge pull request #1636 from mermaid-js/release-promotion
Release live editor
2025-03-15 06:39:13 +05:30
Sidharth Vinod 99255ab693 Merge branch 'develop' into pr/aloisklink/1496
* develop: (101 commits)
  fix: Flickering when rendering, PanZoom & Rough mode combined
  chore: Simplify queueing logic
  fix: Blank screen on initial load
  chore: Add delay for playground toggle
  fix: Record correct diagram type, send playground toggle event immediately.
  chore(deps): update dependency autoprefixer to v10.4.21
  chore(deps): update all non-major dependencies
  chore(deps): update dependency typescript to v5.8.2
  chore(deps): update all non-major dependencies
  build: update Browserslist db
  Use netlify to redirect users
  chore: Add sitemap to robots.txt
  fix: Add redirect for /index.html
  chore: Add sitemap
  fix: Do not index /view page
  chore(deps): update all non-major dependencies
  feat: Pause banner animation when mouse is on the banner
  fix: Layout for banner
  use snippets
  update urls and styling
  ...
2025-03-14 18:08:52 -07:00
Sidharth VinodandGitHub ec40c0369c Merge pull request #1635 from mermaid-js/sidv/panZoomRough
fix: Flickering when rendering, PanZoom & Rough mode combined
2025-03-15 06:36:16 +05:30
Sidharth Vinod a56be6ae14 fix: Flickering when rendering, PanZoom & Rough mode combined 2025-03-14 18:00:12 -07:00
Sidharth VinodandGitHub e29170a1aa Merge pull request #1634 from mermaid-js/release-promotion
Release live editor
2025-03-15 06:02:18 +05:30
Sidharth Vinod d7cd301e15 chore: Simplify queueing logic 2025-03-14 17:26:29 -07:00
Sidharth Vinod 393a5f659e fix: Blank screen on initial load
Queued the rendering pipeline
2025-03-14 15:51:37 -07:00
Sidharth Vinod 71a336c41f fix: Rendering race condition 2025-03-14 15:40:14 -07:00
Sidharth Vinod e394525376 fix: Diagram rendering 2025-03-14 15:20:12 -07:00
Sidharth Vinod 6175561449 chore: Simplify reactivity for tabs 2025-03-14 13:58:31 -07:00
Sidharth Vinod feb3ba88a5 format: eslint + prettier 2025-03-14 11:24:44 -07:00
Sidharth Vinod da75a4951a format: prettier 2025-03-14 11:03:35 -07:00
Sidharth Vinod 02ef7eab78 chore: Simplify docs button 2025-03-14 11:03:10 -07:00
Sidharth Vinod f951ad055b feat: Remove autoSync, add grid toggle 2025-03-13 23:35:59 -07:00
Sidharth Vinod 9e453e84f6 feat: MermaidChart tooltip 2025-03-13 23:17:06 -07:00
Sidharth Vinod 4e7c42f59a fix: Icons 2025-03-13 23:16:56 -07:00
Sidharth Vinod 44e44ede97 feat: MermaidChart tooltip 2025-03-13 22:03:16 -07:00
Sidharth Vinod b624ecb859 feat: Error popup 2025-03-13 20:21:38 -07:00
Sidharth Vinod b13e351460 feat: Full screen view 2025-03-13 19:59:21 -07:00
Sidharth Vinod 30abb2303b fix: Imports 2025-03-13 19:59:08 -07:00
Sidharth Vinod bb29c4af7f fix: Share modal 2025-03-13 19:51:46 -07:00
Sidharth Vinod 30cb61b966 feat: Share modal 2025-03-13 19:50:41 -07:00
Sidharth Vinod 7abe8f82b1 format: prettier 2025-03-13 17:42:52 -07:00
Sidharth Vinod 393e7efbda chore: Simplify history pane 2025-03-13 17:03:19 -07:00
Sidharth Vinod 83715bb250 Merge branch 'develop' into sidv/redesign
* develop:
  chore: Add delay for playground toggle
  fix: Record correct diagram type, send playground toggle event immediately.
  chore(deps): update dependency autoprefixer to v10.4.21
  chore(deps): update all non-major dependencies
  chore(deps): update dependency typescript to v5.8.2
  chore(deps): update all non-major dependencies
  build: update Browserslist db
2025-03-13 15:21:54 -07:00
Sidharth Vinod 6a55d67c4e feat: Tweak links 2025-03-13 15:18:38 -07:00
Sidharth Vinod f16180ec59 feat: Responsiveness 2025-03-13 15:14:42 -07:00
Sidharth Vinod 2883031609 feat: MainMenu 2025-03-13 14:47:13 -07:00
Sidharth Vinod b921101cd5 feat: MainMenu 2025-03-13 13:39:49 -07:00
Sidharth Vinod 1e89e2b4be feat: Privacy popup 2025-03-13 13:22:22 -07:00
Sidharth Vinod 050dafa409 feat: Navbar 2025-03-13 13:07:13 -07:00
Sidharth Vinod 0532e403bf feat: Add history section 2025-03-13 13:01:47 -07:00
Sidharth Vinod 0f3cfbdeac fix: Card icons 2025-03-13 10:26:31 -07:00
Sidharth Vinod 21644297d1 fix: Toolbar spacing and colors 2025-03-13 10:26:14 -07:00
Sidharth Vinod 3d279652b4 format: prettier 2025-03-13 10:23:38 -07:00
Sidharth Vinod c3faf2ff42 fix: Reduce overlap of diagram and toolbars 2025-03-13 10:22:36 -07:00
Sidharth Vinod 72616fca7a fix: Responsiveness of editor 2025-03-13 10:21:02 -07:00
Sidharth VinodandGitHub 51b187be6a Merge pull request #1620 from mermaid-js/release-promotion
Release live editor
2025-03-13 21:54:54 +05:30
Sidharth VinodandGitHub e91e190601 Merge pull request #1629 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-03-13 16:24:09 +00:00
Sidharth VinodandGitHub 40c66ce6e3 Merge pull request #1628 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update dependency autoprefixer to v10.4.21
2025-03-13 16:23:53 +00:00
Sidharth VinodandGitHub 57fc6ede1e Merge pull request #1632 from mermaid-js/sidv/FixAnalytics
fix: Analytics
2025-03-13 21:53:18 +05:30
Sidharth Vinod 62f60867f2 chore: Add delay for playground toggle 2025-03-13 09:22:30 -07:00
Sidharth Vinod 91331bda7e fix: Record correct diagram type, send playground toggle event immediately. 2025-03-13 09:11:46 -07:00
renovate[bot]andGitHub 86c7425271 chore(deps): update dependency autoprefixer to v10.4.21 2025-03-13 15:50:33 +00:00
Sidharth Vinod 73dd347bab 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 dependency typescript to v5.8.2
  chore(deps): update all non-major dependencies
  build: update Browserslist db
2025-03-13 08:46:19 -07:00
renovate[bot]andGitHub b9060ddc31 chore(deps): update all non-major dependencies 2025-03-13 15:02:07 +00:00
Sidharth Vinod fcc7a03650 feat: Migrate to Material icons with Iconify, partial 2025-03-12 21:17:36 -07:00
Sidharth Vinod 2fae6f7798 feat: Floating toolbars 2025-03-11 22:32:59 -07:00
Sidharth Vinod d8cdf2f42c feat: Floating toolbars 2025-03-10 21:28:08 -07:00
Sidharth Vinod f608761fa2 feat: Dark mode 2025-03-10 21:27:50 -07:00
Sidharth Vinod e33aed9cc6 fix: Action panel 2025-03-07 17:00:16 +09:00
Sidharth Vinod 3c2acccf52 fix: Action & Preset stacking 2025-03-07 11:13:30 +07:00
Sidharth Vinod 0cd51750b8 fix: Imports 2025-03-07 03:04:42 +05:30
Sidharth Vinod f6ce710c1b fix: Theme, actions 2025-03-07 03:04:24 +05:30
Sidharth Vinod 433a301776 fix: Preset spacing 2025-03-06 23:17:08 +05:30
Sidharth Vinod bb184a20ca fix: Card padding 2025-03-06 18:53:41 +05:30
Sidharth Vinod 7ca468d42f fix: Actions panel 2025-03-06 18:48:40 +05:30
Sidharth Vinod 5d2f1e4973 fix: Remove overlap of promo 2025-03-06 18:21:18 +05:30
Sidharth Vinod db2e8e0297 fix: Change font 2025-03-06 18:15:20 +05:30
Sidharth Vinod 235d3c252d fix: Sample diagrams 2025-03-06 18:15:08 +05:30
Sidharth Vinod 9934429c4f fix: Cards 2025-03-06 14:30:18 +05:30
Sidharth Vinod 1f55da1f45 fix: Resize handle, diagram view 2025-03-06 12:41:52 +05:30
Sidharth Vinod 9c5ef434dc fix: Resizing, card 2025-03-06 12:24:19 +05:30
Sidharth Vinod 4968e224ae chore: Migrate navbar 2025-03-06 11:58:43 +05:30
Sidharth VinodandGitHub ac812ca1e4 Merge pull request #1619 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency typescript to v5.8.2
2025-03-03 12:31:36 +00:00
renovate[bot]andGitHub c1ecd9cd04 chore(deps): update dependency typescript to v5.8.2 2025-03-03 06:25:23 +00:00
Sidharth VinodandGitHub 68371dae63 Merge pull request #1617 from mermaid-js/browserslist-update
build: update Browserslist db
2025-03-03 06:24:29 +00:00
Sidharth VinodandGitHub 009e4f2390 Merge pull request #1618 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-03-03 06:24:19 +00:00
renovate[bot]andGitHub 50696445cd chore(deps): update all non-major dependencies 2025-03-03 02:46:19 +00:00
GitHub Action bd7ca5a25a build: update Browserslist db 2025-03-01 02:52:22 +00:00
Sidharth Vinod b8aa272bc6 fix: Promo bar 2025-03-01 00:17:09 +05:30
Sidharth Vinod 66566bd6af chore: Add shadcn/button 2025-03-01 00:16:50 +05:30
Sidharth Vinod 3075b63d4a chore: Add shadcn 2025-03-01 00:16:38 +05:30
Sidharth VinodandGitHub c20996488d Merge pull request #1615 from mermaid-js/release-promotion
Release live editor
2025-02-25 07:47:29 +01:00
Sidharth VinodandGitHub 0871de7838 Merge pull request #1613 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-02-25 07:41:56 +01:00
Sidharth VinodandGitHub 20cadcab79 Merge pull request #1614 from mermaid-js/sidv/indexing
Indexing fixes
2025-02-25 07:41:44 +01:00
Sidharth Vinod fb5894e093 Use netlify to redirect users 2025-02-25 12:08:54 +05:30
Sidharth Vinod fa0498f75d chore: Add sitemap to robots.txt 2025-02-25 08:54:26 +05:30
Sidharth Vinod cdc7f9faa4 fix: Add redirect for /index.html 2025-02-25 08:50:03 +05:30
Sidharth Vinod 51591ba075 chore: Add sitemap 2025-02-25 08:49:46 +05:30
Sidharth Vinod 78ccc2ff4c fix: Do not index /view page 2025-02-25 08:49:25 +05:30
renovate[bot]andGitHub fe8e51fa79 chore(deps): update all non-major dependencies 2025-02-24 19:55:23 +00:00
Sidharth VinodandGitHub e0b660d56b Merge pull request #1610 from mermaid-js/release-promotion
Release live editor
2025-02-21 22:43:23 +05:30
Sidharth VinodandGitHub 03404e8e59 Merge pull request #1611 from mermaid-js/promos
Update promos
2025-02-21 17:06:40 +00:00
Sidharth Vinod edc36351d4 feat: Pause banner animation when mouse is on the banner 2025-02-21 21:23:21 +05:30
Sidharth Vinod 66c155bab7 fix: Layout for banner 2025-02-21 21:02:47 +05:30
Steph ee3f4420f7 use snippets 2025-02-21 05:41:03 -08:00
Sidharth VinodandGitHub b35ce74065 Merge pull request #1606 from mermaid-js/browserslist-update
build: update Browserslist db
2025-02-21 13:44:41 +05:30
Sidharth VinodandGitHub 31616cadc0 Merge pull request #1607 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-02-21 08:14:21 +00:00
Sidharth VinodandGitHub 7389a9fdad Merge pull request #1608 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2025-02-21 13:44:08 +05:30
Steph 96c8333bbb update urls and styling 2025-02-20 16:01:49 -08:00
renovate[bot]andGitHub f45b04e16a chore(deps): update all non-major dependencies 2025-02-20 12:31:22 +00:00
renovate[bot]andGitHub 5c0c471ff6 chore(deps): update all non-major dependencies 2025-02-18 18:48:44 +00:00
GitHub Action fccfd6fbe2 build: update Browserslist db 2025-02-15 02:40:36 +00:00
Sidharth VinodandGitHub 9a70c08a6f Merge pull request #1602 from mermaid-js/release-promotion
Release live editor
2025-02-14 13:46:19 +05:30
Sidharth VinodandGitHub 77234d95b3 Merge pull request #1605 from mermaid-js/update-promos
Update promos
2025-02-14 13:45:46 +05:30
Sidharth Vinod 1df59ae5dd chore: Cleanup class 2025-02-14 13:39:04 +05:30
Sidharth Vinod 7e043e7e7e Merge branch 'update-promos' of https://github.com/mermaid-js/mermaid-live-editor into update-promos
* 'update-promos' of https://github.com/mermaid-js/mermaid-live-editor:
  update interval
2025-02-14 13:27:30 +05:30
Sidharth Vinod 8ddf56191d chore: Add padding to link 2025-02-14 13:27:22 +05:30
Steph 942840fb62 update interval 2025-02-13 15:41:19 -08:00
Sidharth Vinod bb224b486a fix: Position of text when switching, data structure for links 2025-02-13 18:26:26 +05:30
Steph 23bb5d63dc update urls 2025-02-13 03:57:41 -08:00
Steph abf80da2c1 wip 2025-02-13 03:46:28 -08:00
Sidharth VinodandGitHub 276b49e554 Merge pull request #1601 from mermaid-js/sidv/hidePromotions
Timebox Hidden Promotions
2025-02-05 19:04:05 +05:30
Sidharth VinodandGitHub bb7600f8c5 Merge pull request #1579 from mermaid-js/renovate/all-minor-patch
fix(deps): update all non-major dependencies (minor)
2025-02-04 23:44:47 +05:30
Sidharth Vinod 0b1dd4b06d chore: Cleanup 2025-02-04 23:41:48 +05:30
Sidharth Vinod dc815cdc4d feat: Add hide duration for promotions 2025-02-04 23:39:59 +05:30
Sidharth VinodandGitHub c5eaf340b4 Merge pull request #1595 from mermaid-js/release-promotion
Release live editor
2025-02-04 21:00:21 +05:30
renovate[bot]andGitHub 4cead1811f fix(deps): update all non-major dependencies 2025-02-04 15:28:23 +00:00
Sidharth Vinod 1b7065609b Merge branch 'master' into develop
* master:
  update end date
  update-promo
2025-02-04 20:57:19 +05:30
Sidharth VinodandGitHub e98a7b8647 Merge pull request #1585 from mermaid-js/renovate/patch-mermaid-packages
fix(deps): update dependency mermaid to v11.4.1
2025-02-04 20:56:23 +05:30
renovate[bot]andGitHub 922f82cc31 fix(deps): update dependency mermaid to v11.4.1 2025-02-04 15:26:02 +00:00
Sidharth VinodandGitHub f5f5038c69 Merge pull request #1599 from mermaid-js/browserslist-update
build: update Browserslist db
2025-02-04 20:55:23 +05:30
Sidharth VinodandGitHub d6288608be Merge pull request #1600 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-02-04 20:55:09 +05:30
renovate[bot]andGitHub d36571491f chore(deps): update all non-major dependencies 2025-02-04 12:46:37 +00:00
Sidharth Vinod a7e14c77cb Update banner timing 2025-02-04 18:03:13 +05:30
GitHub Action dab0e8880f build: update Browserslist db 2025-02-01 02:41:49 +00:00
Sidharth VinodandGitHub f2e2fc4e11 Merge pull request #1583 from mermaid-js/renovate/npm-sveltejs-kit-vulnerability
chore(deps): update dependency @sveltejs/kit to v2.8.3 [security]
2025-01-27 19:05:15 +05:30
Sidharth VinodandGitHub 54b7655015 Merge pull request #1592 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v5.4.12 [security]
2025-01-27 19:05:01 +05:30
Sidharth VinodandGitHub 681f434574 Merge pull request #1596 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2025-01-27 19:04:49 +05:30
renovate[bot]andGitHub 2d7252c640 chore(deps): update all non-major dependencies 2025-01-27 00:36:08 +00:00
renovate[bot]andGitHub e97c727170 chore(deps): update dependency vite to v5.4.12 [security] 2025-01-26 04:39:11 +00:00
renovate[bot]andGitHub 4233a1e21e chore(deps): update dependency @sveltejs/kit to v2.8.3 [security] 2025-01-26 04:39:02 +00:00
Sidharth VinodandGitHub 8735b0f52b Merge pull request #1575 from mermaid-js/browserslist-update
build: update Browserslist db
2025-01-26 10:07:45 +05:30
Sidharth VinodandGitHub 975e34b2fd Merge pull request #1578 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2025-01-26 10:07:34 +05:30
renovate[bot]andGitHub c77fc8c979 fix(deps): update all non-major dependencies 2025-01-23 20:30:38 +00:00
GitHub Action f3b62ce674 build: update Browserslist db 2025-01-15 02:39:27 +00:00
Sidharth VinodandGitHub cc562549a9 Merge pull request #1590 from mermaid-js/update-promo-bar
Update promo bar
2025-01-06 12:17:16 +05:30
Steph 2438363c03 update end date 2025-01-03 12:02:44 -08:00
Steph bd9625a3f9 update-promo 2025-01-03 10:39:00 -08:00
GitHub Action 7df3df748b build: update Browserslist db 2025-01-01 02:51:07 +00:00
GitHub Action e37253645c build: update Browserslist db 2024-12-15 03:02:55 +00:00
GitHub Action 502e0b5fc3 build: update Browserslist db 2024-12-01 03:09:36 +00:00
GitHub Action 13d7f878f1 build: update Browserslist db 2024-11-15 02:52:27 +00:00
Sidharth VinodandGitHub bb0ab7c7e2 Merge pull request #1574 from mermaid-js/release-promotion
Release live editor
2024-11-14 20:44:46 +05:30
Sidharth VinodandGitHub 939ef8b56e Merge pull request #1572 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-11-14 20:42:14 +05:30
renovate[bot]andGitHub 1e6e994413 chore(deps): update all non-major dependencies 2024-11-14 15:05:37 +00:00
Sidharth VinodandGitHub ba1c0da871 Merge pull request #1573 from mermaid-js/playground
MC Playground Toggle
2024-11-14 20:34:40 +05:30
Sidharth Vinod 5b103acb11 chore: Update snapshots 2024-11-14 19:09:42 +05:30
Sidharth Vinod b658ceedc9 chore: Update URL 2024-11-14 17:27:10 +05:30
Sidharth Vinod dc114d55c9 fix: Unit test 2024-11-14 13:53:28 +05:30
Sidharth Vinod 90716ef8d5 chore: Update labels 2024-11-14 13:50:46 +05:30
Sidharth Vinod f954dfab61 feat: Retain toggle label positions 2024-11-13 21:07:57 +05:30
Sidharth VinodandGitHub 877cbdb6f9 Merge pull request #1570 from mermaid-js/release-promotion
Release live editor
2024-11-12 00:14:29 +05:30
Sidharth Vinod 09e705c3ae Merge branch 'develop' into playground
* develop:
  fix: Add fallback for state
  update whiteboard tagline
  remove august promo
  update dates
  update taglines
  remove product hunt promo
  add and update product hunt promo for whiteboard
  add product hunt badge to navbar
2024-11-11 16:56:59 +05:30
Sidharth Vinod 83f3c07f10 Merge branch 'master' into develop
* master:
  update whiteboard tagline
  remove august promo
  update dates
  update taglines
  remove product hunt promo
  add and update product hunt promo for whiteboard
  add product hunt badge to navbar
2024-11-11 16:55:26 +05:30
Sidharth Vinod 3e3a0162bf fix: Add fallback for state 2024-11-11 16:54:03 +05:30
Sidharth Vinod 8c1c348f68 chore: Cleanup 2024-11-11 01:47:43 +05:30
Sidharth Vinod f7f3e35fcf feat: Add dropdown navbar items 2024-11-11 01:45:12 +05:30
Sidharth Vinod 5acd3faacc fix: Add fallback for state 2024-11-08 22:46:54 +05:30
Sidharth Vinod 4c24b52e12 feat: Add mermaidchart playground toggle 2024-11-08 22:30:47 +05:30
Sidharth VinodandGitHub 8fd965624f Merge pull request #1565 from mermaid-js/sidv/svelte5
Svelte 5
2024-11-08 22:16:16 +05:30
Sidharth Vinod e2d7fd46ac chore: Update action versions 2024-11-08 22:00:52 +05:30
Sidharth Vinod 09616ed156 chore: Fix unit test 2024-11-08 21:42:57 +05:30
Sidharth Vinod 118f03385e Merge branch 'develop' into sidv/svelte5
* develop:
  chore(deps): update all non-major dependencies
  chore(deps): update dependency vite to v5.4.6 [security]
  fix(deps): update all non-major dependencies
  test: increase file size limit
  fix(deps): update dependency mermaid to v11.4.0
  build: update Browserslist db
2024-11-08 21:36:10 +05:30
Sidharth VinodandGitHub ee156c7e69 Merge pull request #1543 from mermaid-js/renovate/npm-vite-vulnerability
chore(deps): update dependency vite to v5.4.6 [security]
2024-11-08 21:19:18 +05:30
Sidharth VinodandGitHub e80f0865f5 Merge pull request #1539 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-11-08 21:19:01 +05:30
renovate[bot]andGitHub aa9e9c8a23 chore(deps): update all non-major dependencies 2024-11-08 10:15:08 +00:00
renovate[bot]andGitHub 75697b7ce1 chore(deps): update dependency vite to v5.4.6 [security] 2024-11-08 10:14:47 +00:00
Sidharth VinodandGitHub 98f7b1b3d1 Merge pull request #1546 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2024-11-08 15:44:00 +05:30
renovate[bot]andGitHub 84db2c7c6a fix(deps): update all non-major dependencies 2024-11-08 10:01:04 +00:00
Sidharth VinodandGitHub d8401a8f13 Merge pull request #1563 from mermaid-js/release-promotion
Release live editor
2024-11-08 15:29:57 +05:30
Sidharth VinodandGitHub 6816bd986c Merge pull request #1567 from mermaid-js/browserslist-update
build: update Browserslist db
2024-11-08 15:29:32 +05:30
Sidharth VinodandGitHub 95c858936d Merge pull request #1568 from mermaid-js/renovate/mermaid-packages
fix(deps): update dependency mermaid to v11.4.0
2024-11-08 15:29:20 +05:30
Sidharth Vinod c67b84a458 test: increase file size limit 2024-11-08 14:52:57 +05:30
Sidharth VinodandGitHub 765c35265f Merge pull request #1564 from mermaid-js/update-whiteboard-tagline
Promo bar: update Whiteboard tagline
2024-11-06 11:39:57 +05:30
renovate[bot]andGitHub ecc34aabc5 fix(deps): update dependency mermaid to v11.4.0 2024-11-04 02:04:17 +00:00
GitHub Action 6517053b8e build: update Browserslist db 2024-11-01 02:54:51 +00:00
Sidharth Vinod 190bac8c70 Svelte 5 migration 2024-10-30 23:38:20 +05:30
Steph cfdb4fbf61 update whiteboard tagline 2024-10-30 09:20:01 -07:00
Sidharth Vinod 8cf4ac432d Allow props 2024-10-30 01:33:03 +05:30
Sidharth VinodandGitHub 5ba65bc032 Merge pull request #1557 from mermaid-js/browserslist-update
build: update Browserslist db
2024-10-30 00:33:48 +05:30
Sidharth VinodandGitHub 2fe3df56b7 Merge pull request #1559 from mermaid-js/promo-updates
Remove Product Hunt promos and Update taglines
2024-10-22 20:10:23 +05:30
Steph 659e82e9ae remove august promo 2024-10-22 07:35:08 -07:00
Steph 76c937a878 update dates 2024-10-22 00:12:07 -07:00
Steph 98cbcf1abe update taglines 2024-10-22 00:07:40 -07:00
Steph 6bdabf9325 remove product hunt promo 2024-10-21 23:32:40 -07:00
GitHub Action 4c06db65f4 build: update Browserslist db 2024-10-15 02:49:53 +00:00
Sidharth VinodandGitHub cc963db471 Merge pull request #1554 from mermaid-js/release-promotion
Release live editor
2024-10-05 09:49:22 +05:30
Sidharth VinodandGitHub 8190a9433f Merge pull request #1528 from bluprince13/feature/1511_ability-to-disable-data-share-buttons
Disable buttons/links to share data with other websites depending on environment variables
2024-10-04 22:55:04 +05:30
Sidharth VinodandGitHub 4415849a6d Merge branch 'develop' into feature/1511_ability-to-disable-data-share-buttons 2024-10-04 14:45:39 +05:30
Sidharth VinodandGitHub d5123c6a26 Merge pull request #1553 from mermaid-js/release-promotion
Release live editor
2024-10-03 21:19:19 +05:30
Sidharth Vinod 60e2b307e9 Bump mermaid@11.3.0 2024-10-03 21:17:14 +05:30
Sidharth VinodandGitHub 1c04d7d77b Merge pull request #1551 from mermaid-js/browserslist-update
build: update Browserslist db
2024-10-03 21:16:21 +05:30
Sidharth VinodandGitHub f59a2dc6d7 Merge pull request #1552 from mermaid-js/promo-whiteboard
Product Hunt promo - Whiteboard
2024-10-03 15:55:12 +05:30
Steph 3b0fae39c9 add and update product hunt promo for whiteboard 2024-10-03 01:50:54 -07:00
GitHub Action 6e262b3ea4 build: update Browserslist db 2024-10-01 02:54:11 +00:00
Sidharth Vinod 267c6160be chore: Remove unused destructuring 2024-09-29 12:46:27 +05:30
8fb2114831 Update src/lib/util/promos/promo.ts
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2024-09-28 18:19:51 +01:00
7bae587c8c Update src/routes/edit/+page.svelte
Co-authored-by: Sidharth Vinod <github@sidharth.dev>
2024-09-28 18:19:39 +01:00
Vipin Ajayakumar f66331852e Disable buttons/links to share data with other websites depending on env variables 2024-09-28 16:12:43 +01:00
Sidharth VinodandGitHub c06828dbf2 Merge pull request #1541 from mermaid-js/release-promotion
Release live editor
2024-09-16 22:15:28 +05:30
Sidharth Vinod e4092a6be2 chore: Update mermaid to 11.2.1 2024-09-16 22:14:18 +05:30
Sidharth VinodandGitHub 9ffa715d03 Merge pull request #1540 from mermaid-js/release-promotion
Release live editor
2024-09-16 11:31:39 +05:30
Sidharth VinodandGitHub 8fdd959079 Merge pull request #1538 from mermaid-js/browserslist-update
build: update Browserslist db
2024-09-16 11:20:22 +05:30
Sidharth VinodandGitHub 887545323d Merge pull request #1534 from nnmrts/develop
update mermaid dependencies
2024-09-16 05:45:47 +00:00
Sidharth Vinod bc72088fe9 chore: Fix test 2024-09-16 11:14:16 +05:30
GitHub Action 2077d37d3a build: update Browserslist db 2024-09-15 02:45:48 +00:00
Nano Miratus 4781d46c03 update dependencies 2024-09-09 21:38:28 +02:00
Sidharth VinodandGitHub 4a08f6dad5 Merge pull request #1527 from mermaid-js/add-product-hunt-badge
Add Product Hunt badge
2024-09-09 09:15:51 +05:30
Steph 806699d478 add product hunt badge to navbar 2024-09-06 08:20:05 -07:00
Sidharth VinodandGitHub 07595b54a6 Merge pull request #1526 from mermaid-js/release-promotion
Release live editor
2024-09-05 19:21:15 +05:30
Sidharth VinodandGitHub 370a03f708 Merge pull request #1524 from mermaid-js/browserslist-update
build: update Browserslist db
2024-09-05 09:57:54 +05:30
Sidharth VinodandGitHub e88bb8ba30 Merge pull request #1508 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2024-09-05 09:57:41 +05:30
Sidharth VinodandGitHub b3a3a0ed51 Merge pull request #1525 from mermaid-js/update-promo-bar-button-style
Update promo bar styling
2024-09-05 09:57:24 +05:30
Steph fbf2bcb202 update promo bar styling 2024-09-04 18:19:40 -07:00
renovate[bot]andGitHub 762f8f00a3 fix(deps): update all non-major dependencies 2024-09-04 19:07:59 +00:00
GitHub Action 21ba23d95d build: update Browserslist db 2024-09-01 02:47:28 +00:00
Sidharth VinodandGitHub 4d4dde003b Merge pull request #1519 from mermaid-js/release-promotion
Release live editor
2024-08-28 18:05:15 +05:30
Ashish JainandGitHub eaf8eb4245 Merge pull request #1518 from mermaid-js/sidv/elkLoader
feat: Add layout-elk
2024-08-28 12:24:21 +00:00
Sidharth Vinod d023a1142b feat: Add layout-elk 2024-08-28 17:43:37 +05:30
Sidharth VinodandGitHub 571c58e5b9 Merge pull request #1513 from mermaid-js/release-promotion
Release live editor
2024-08-24 15:59:30 +05:30
Sidharth VinodandGitHub 3fbe4a80c8 Merge pull request #1512 from mermaid-js/sidv/MermaidV11
chore: Release mermaid v11
2024-08-24 15:59:02 +05:30
Sidharth Vinod 2cdc8ed09e Merge branch 'develop' into sidv/MermaidV11
* develop:
  chore(deps): update all non-major dependencies
2024-08-24 14:39:05 +05:30
Sidharth VinodandGitHub 48333cab3b Merge pull request #1509 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-08-24 14:37:35 +05:30
Sidharth Vinod dd7ff496c7 Update lockfile 2024-08-24 14:36:59 +05:30
Sidharth Vinod 34207c76b3 Merge branch 'develop' into sidv/MermaidV11
* develop:
  build: update Browserslist db
2024-08-24 14:35:51 +05:30
renovate[bot]andGitHub 15bb56bd7e chore(deps): update all non-major dependencies 2024-08-24 08:55:48 +00:00
Sidharth VinodandGitHub 634921dd65 Merge pull request #1510 from mermaid-js/browserslist-update
build: update Browserslist db
2024-08-24 14:23:49 +05:30
Sidharth Vinod c77ccfeaba chore: Release mermaid v11 2024-08-24 14:23:06 +05:30
GitHub Action 79651594ed build: update Browserslist db 2024-08-15 02:29:13 +00:00
Sidharth VinodandGitHub c40d83d068 Merge pull request #1504 from mermaid-js/release-promotion
Release live editor
2024-08-07 22:11:50 +05:30
Sidharth VinodandGitHub c3fae5817b Merge pull request #1499 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-08-07 16:40:48 +00:00
Sidharth VinodandGitHub f736e1545e Merge pull request #1506 from mermaid-js/update-promo-bar
update promo bar
2024-08-07 10:59:09 +05:30
Sidharth Vinod 2916eaedaa chore: Simplify promotion 2024-08-07 10:22:45 +05:30
renovate[bot]andGitHub d529226922 chore(deps): update all non-major dependencies 2024-08-06 16:32:55 +00:00
Sidharth VinodandGitHub 910fdd76d8 Merge pull request #1505 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-08-06 22:02:11 +05:30
Steph 9dc4f5773c update promo bar 2024-08-06 04:27:25 -07:00
renovate[bot]andGitHub 4aad53032f chore(deps): update all non-major dependencies 2024-08-06 07:30:33 +00:00
Sidharth VinodandGitHub 2815f35334 Merge pull request #1503 from mermaid-js/browserslist-update
build: update Browserslist db
2024-08-02 12:43:19 +00:00
Sidharth VinodandGitHub 82b9bfe64a Merge branch 'develop' into fix/stop-parsing-syntax-error-as-html 2024-08-02 18:12:38 +05:30
Sidharth VinodandGitHub 96b20fc12c Merge pull request #1498 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-08-02 12:42:09 +00:00
renovate[bot]andGitHub 36f9cfbb4a chore(deps): update all non-major dependencies 2024-08-01 13:01:01 +00:00
GitHub Action a01fe0b044 build: update Browserslist db 2024-08-01 02:35:24 +00:00
Sidharth VinodandGitHub 29d0feaab1 Merge pull request #1501 from mermaid-js/product-hunt-updates
Remove Product Hunt text and badge
2024-07-31 10:12:56 +05:30
Steph 6e16716376 remove product hunt text and badge 2024-07-29 00:27:48 -07:00
Alois KlinkandGitHub b58577b72d fix: use <output> and <pre> to format errors
Use the `<output>` and `<pre>` HTML elements to format mermaid errors,
instead just adding the error message as HTML.

Using `<pre>` avoids the need for sanitizing any HTML (or using a
`monospace` font in CSS, since `<pre>` does this automatically).

Using `<output>` means that users using screenreaders should have a
better experience when using the mermaid live editor.

on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
2024-07-26 17:30:39 +09:00
Alois Klink 0671a9b2f1 Revert "feat: Sanitize error message"
This reverts commit 996a20d90e.

A better way to do fix this bug would be to not parse the syntax error
as HTML in the first place.
2024-07-26 17:20:15 +09:00
Sidharth VinodandGitHub ff092df72f Merge pull request #1495 from mermaid-js/release-promotion
Release live editor
2024-07-25 18:39:20 +05:30
Sidharth VinodandGitHub 28ef0e07d3 Merge pull request #1494 from mermaid-js/sidv/sanitizeError
feat: Sanitize error message
2024-07-25 18:38:12 +05:30
Sidharth Vinod 996a20d90e feat: Sanitize error message 2024-07-24 11:26:57 +05:30
Sidharth VinodandGitHub 5b1979d420 Merge pull request #1485 from mermaid-js/release-promotion
Release live editor
2024-07-24 10:46:45 +05:30
Sidharth VinodandGitHub eb62ba0b16 Merge pull request #1490 from mermaid-js/renovate/all-minor-patch
chore(deps): update dependency eslint-plugin-svelte to v2.43.0
2024-07-24 10:46:06 +05:30
Sidharth VinodandGitHub 46154948bb Merge pull request #1489 from mermaid-js/renovate/patch-all-minor-patch
fix(deps): update all non-major dependencies (patch)
2024-07-24 10:45:48 +05:30
Sidharth VinodandGitHub cbeb633895 Merge pull request #1492 from mermaid-js/update-product-hunt-content
Update Product Hunt content
2024-07-24 10:45:03 +05:30
Steph 1da5938ff2 update product hunt content 2024-07-23 16:28:50 -07:00
renovate[bot]andGitHub 5621afc874 fix(deps): update all non-major dependencies 2024-07-22 23:52:38 +00:00
renovate[bot]andGitHub d4602888f7 chore(deps): update dependency eslint-plugin-svelte to v2.43.0 2024-07-22 00:15:02 +00:00
Sidharth VinodandGitHub 3a3641b9d0 Merge pull request #1486 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-07-17 05:17:27 +00:00
Sidharth VinodandGitHub 84d666f558 Merge pull request #1487 from mermaid-js/browserslist-update
build: update Browserslist db
2024-07-17 05:17:19 +00:00
renovate[bot]andGitHub 2133625261 chore(deps): update all non-major dependencies 2024-07-16 18:35:04 +00:00
GitHub Action 34ae0ab1c9 build: update Browserslist db 2024-07-15 02:33:10 +00:00
Sidharth VinodandGitHub a782f60d08 Merge pull request #1484 from mermaid-js/master
Master
2024-07-14 09:32:22 +05:30
Sidharth VinodandGitHub 32a90daaeb Merge pull request #1477 from mermaid-js/release-promotion
Release live editor
2024-07-14 09:31:45 +05:30
Sidharth VinodandGitHub e9c11a9e1e Merge pull request #1469 from mermaid-js/dependabot/npm_and_yarn/ws-8.17.1
build(deps): bump ws from 8.16.0 to 8.17.1
2024-07-14 03:58:43 +00:00
Sidharth VinodandGitHub 0c5002bdad Merge pull request #1471 from mermaid-js/renovate/patch-all-minor-patch
chore(deps): update all non-major dependencies (patch)
2024-07-14 03:58:33 +00:00
Sidharth VinodandGitHub 394206340b Merge pull request #1478 from mermaid-js/browserslist-update
build: update Browserslist db
2024-07-14 09:28:08 +05:30
Sidharth VinodandGitHub 6db6b6d8cd Merge pull request #1481 from allxiao/patch-1
Update manifest.json to align the PWA scope with the editor URL, fix #1480
2024-07-14 09:27:54 +05:30
renovate[bot]andGitHub c4b848efd4 chore(deps): update all non-major dependencies 2024-07-13 12:49:17 +00:00
Menghua XiaoandGitHub e66630f6e1 Update manifest.json to align the URL with the PWA scope, fix #1480 2024-07-09 12:28:13 +08:00
GitHub Action 903106d34b build: update Browserslist db 2024-07-01 02:34:12 +00:00
dependabot[bot]andGitHub d1da3ac52c build(deps): bump ws from 8.16.0 to 8.17.1
Bumps [ws](https://github.com/websockets/ws) from 8.16.0 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.16.0...8.17.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-27 06:07:58 +00:00
Sidharth VinodandGitHub 3b8209b3fe Merge pull request #1475 from mermaid-js/dependabot/npm_and_yarn/zenuml/core-3.23.26
build(deps): bump @zenuml/core from 3.17.3 to 3.23.26
2024-06-27 06:05:55 +00:00
Sidharth VinodandGitHub ea2d8e843c Merge pull request #1472 from mermaid-js/renovate/all-minor-patch
chore(deps): update all non-major dependencies (minor)
2024-06-27 06:05:45 +00:00
Sidharth VinodandGitHub 8909b9e54d Merge pull request #1476 from mermaid-js/add-product-hunt-badge
Add Product Hunt badge
2024-06-27 11:20:47 +05:30
steph 62906cba24 add product hunt badge to navbar 2024-06-26 22:42:28 -07:00
dependabot[bot]andGitHub 2a4a700614 build(deps): bump @zenuml/core from 3.17.3 to 3.23.26
Bumps [@zenuml/core](https://github.com/mermaid-js/zenuml-core) from 3.17.3 to 3.23.26.
- [Commits](https://github.com/mermaid-js/zenuml-core/compare/v3.17.3...v3.23.26)

---
updated-dependencies:
- dependency-name: "@zenuml/core"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-26 19:09:53 +00:00
renovate[bot]andGitHub 8a2bfcf02e chore(deps): update all non-major dependencies 2024-06-24 14:30:04 +00:00
Sidharth VinodandGitHub e9a0378c2c Merge pull request #1470 from mermaid-js/promo-bar
Promo bar - update end date
2024-06-19 23:22:38 +05:30
Steph e703aec424 update endDate on promo 2024-06-19 10:49:13 -07:00
Sidharth VinodandGitHub 582c9662f2 Merge pull request #1468 from mermaid-js/release-promotion
Release live editor
2024-06-17 10:56:21 +05:30
Sidharth VinodandGitHub 6d67f0cd42 Merge pull request #1300 from mermaid-js/sidv/errorHighlight
feat: Add error highlight
2024-06-17 04:44:36 +00:00
Sidharth Vinod aca7f07aa8 Merge branch 'develop' into sidv/errorHighlight
* develop: (148 commits)
  Remove error lines from View
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  Remove stalebot
  build: update Browserslist db
  build: update Browserslist db
  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
  ...
2024-06-17 10:10:01 +05:30
Sidharth VinodandGitHub 368e741675 Merge pull request #1426 from mermaid-js/dependabot/npm_and_yarn/katex-0.16.10
build(deps): bump katex from 0.16.9 to 0.16.10
2024-06-17 04:38:48 +00:00
Sidharth VinodandGitHub 9695512981 Merge pull request #1299 from mermaid-js/sidv/errorDialogue
feat: Move error dialogue to bottom of editor.
2024-06-17 04:38:33 +00:00
Sidharth Vinod 3baba33e65 Remove error lines from View 2024-06-16 20:22:23 +05:30
Sidharth Vinod 49edf2e8e2 Merge branch 'develop' into sidv/errorDialogue
* develop: (139 commits)
  chore(deps): update all non-major dependencies
  chore(deps): update all non-major dependencies
  Remove stalebot
  build: update Browserslist db
  build: update Browserslist db
  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
  ...
2024-06-16 20:18:09 +05:30
Sidharth VinodandGitHub beca75bbba Merge branch 'develop' into dependabot/npm_and_yarn/katex-0.16.10 2024-06-16 20:08:41 +05:30
Sidharth VinodandGitHub ea0553342c Merge pull request #1458 from mermaid-js/release-promotion
Release live editor
2024-05-23 18:27:12 +05:30
Sidharth VinodandGitHub 8b673659d6 Merge pull request #1448 from mermaid-js/release-promotion
Release live editor
2024-05-21 12:40:59 +05:30
Sidharth VinodandGitHub 994a87b103 Merge branch 'master' into release-promotion 2024-05-21 12:40:44 +05:30
Sidharth VinodandGitHub f7046c27c5 Merge pull request #1437 from mermaid-js/release-promotion
Release live editor
2024-05-12 09:22:11 +05:30
Sidharth VinodandGitHub fdd2581b64 Merge pull request #1430 from mermaid-js/release-promotion
Release live editor
2024-04-05 19:07:24 +05:30
Sidharth VinodandGitHub 8fd1c154d6 Merge pull request #1417 from mermaid-js/release-promotion
Release live editor
2024-04-03 00:42:40 +05:30
dependabot[bot]andGitHub f89b2f5698 build(deps): bump katex from 0.16.9 to 0.16.10
Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.9 to 0.16.10.
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.9...v0.16.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 20:31:18 +00:00
Sidharth VinodandGitHub 6de7cbc520 Merge pull request #1407 from mermaid-js/release-promotion
Release live editor
2024-03-05 23:09:29 +05:30
Sidharth VinodandGitHub f7ae8d19e6 Merge pull request #1409 from mermaid-js/update-announcement-bar
Announcement Bar - update link
2024-03-05 23:02:31 +05:30
steph 611fda5eff update link 2024-02-28 21:42:02 -08:00
Sidharth VinodandGitHub c0101cdd4f Merge pull request #1400 from mermaid-js/release-promotion
Release live editor
2024-02-12 23:09:19 +05:30
Sidharth VinodandGitHub 4f66f40779 Merge pull request #1397 from mermaid-js/release-promotion
Release live editor
2024-02-09 11:19:44 +05:30
Sidharth VinodandGitHub 552df747fd Merge pull request #1395 from mermaid-js/release-promotion
Release live editor
2024-02-08 08:04:47 +05:30
Sidharth VinodandGitHub 2e25c8475a Merge pull request #1392 from mermaid-js/release-promotion
Release live editor
2024-02-03 11:09:38 +05:30
Sidharth VinodandGitHub 0abf806f24 Merge branch 'master' into release-promotion 2024-02-03 11:09:05 +05:30
Sidharth VinodandGitHub f4d24c5ab8 Merge pull request #1387 from mermaid-js/release-promotion
Release live editor
2024-01-30 23:42:44 +05:30
Sidharth VinodandGitHub d0da4a07b4 Merge pull request #1374 from mermaid-js/release-promotion
Release live editor
2024-01-23 13:46:03 +05:30
Sidharth VinodandGitHub d8ba3747bf Merge pull request #1373 from mermaid-js/release-promotion
Release live editor
2024-01-15 12:31:41 +05:30
Sidharth VinodandGitHub 9a15de1467 Merge pull request #1370 from mermaid-js/release-promotion
Release live editor
2024-01-11 22:44:43 +05:30
Sidharth VinodandGitHub 9e0fa86127 Merge pull request #1369 from mermaid-js/release-promotion
Release live editor
2024-01-11 22:15:16 +05:30
Sidharth Vinod bb526e1534 test: Ignore monaco internal error. 2023-09-26 12:11:48 +05:30
Sidharth Vinod 8cb7bffc28 chore: Fix test without ID 2023-09-26 11:32:40 +05:30
Sidharth Vinod 4fda534c6a chore: Add id to fix test 2023-09-26 10:56:49 +05:30
Sidharth Vinod f64eaaa528 refactor: Remove unnecessary div 2023-09-26 10:54:42 +05:30
Sidharth Vinod 4994ecf1dd feat: Add error highlight 2023-09-15 13:17:58 +05:30
Sidharth Vinod 3ce91d9d22 chore: Simplify classes 2023-09-15 11:38:07 +05:30
Sidharth Vinod 182b488552 Increase max height 2023-09-15 11:21:22 +05:30
Sidharth Vinod 17f27de9bd feat: Move error dialogue to bottom of editor. 2023-09-15 11:12:44 +05:30
145 changed files with 12208 additions and 9787 deletions
+7 -2
View File
@@ -1,2 +1,7 @@
MERMAID_DOMAIN="mermaid.live"
MERMAID_ANALYTICS_URL="https://p.mermaid.live"
MERMAID_DOMAIN=''
MERMAID_ANALYTICS_URL=''
MERMAID_RENDERER_URL='https://mermaid.ink'
MERMAID_KROKI_RENDERER_URL='https://kroki.io'
MERMAID_IS_ENABLED_MERMAID_CHART_LINKS=''
# cp .env .env.local to make local changes
+26 -2
View File
@@ -11,7 +11,15 @@ module.exports = {
'plugin:svelte/prettier',
'prettier'
],
plugins: ['tailwindcss', '@typescript-eslint', 'es', 'vitest', 'no-only-tests', 'unicorn'],
plugins: [
'tailwindcss',
'@typescript-eslint',
'es',
'vitest',
'no-only-tests',
'sort-keys',
'unicorn'
],
ignorePatterns: [
'docs/*',
'*.cjs',
@@ -40,6 +48,15 @@ module.exports = {
'plugin:@typescript-eslint/strict',
'prettier'
]
},
{
files: ['**/components/ui/**'],
rules: {
'unicorn/prefer-export-from': 'off',
'unicorn/prevent-abbreviations': 'off',
'unicorn/explicit-length-check': 'off',
'sort-keys/sort-keys-fix': 'off'
}
}
],
parserOptions: {
@@ -54,6 +71,7 @@ module.exports = {
es2020: true
},
rules: {
'sort-keys/sort-keys-fix': ['error', 'asc', { minKeys: 5 }],
'@typescript-eslint/ban-ts-comment': [
'error',
{
@@ -77,17 +95,23 @@ module.exports = {
'error',
{
allowList: {
args: true,
ctx: true,
db: true,
doc: true,
env: true,
fn: true,
i: true,
j: true,
k: true,
param: true,
Params: true,
params: true,
Props: true,
props: true,
req: true,
res: true,
str: true,
searchParams: true,
temp: true,
ImportMetaEnv: true
}
+35 -14
View File
@@ -14,28 +14,49 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Install pnpm
uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 18
cache: yarn
node-version-file: '.node-version'
cache: pnpm
- name: Setup Pages
uses: actions/configure-pages@v5
with:
static_site_generator: sveltekit
- name: Build & Deploy
env:
MERMAID_DOMAIN: 'mermaid.live'
MERMAID_ANALYTICS_URL: 'https://p.mermaid.live'
MERMAID_RENDERER_URL: 'https://mermaid.ink'
MERMAID_KROKI_RENDERER_URL: 'https://kroki.io'
MERMAID_IS_ENABLED_MERMAID_CHART_LINKS: 'true'
run: |
export DEPLOY=true
[ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/
yarn install
version=$(yarn version --patch --no-git-tag-version | grep "New version" | cut -d':' -f 2)
yarn build
yarn run lint
cd ..
pnpm install
pnpm build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/master' }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
keep_files: true
path: ./docs
deploy:
if: ${{ github.ref == 'refs/heads/master' }}
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+30 -27
View File
@@ -7,45 +7,48 @@ on:
- develop
jobs:
cypress-run:
playwright:
name: 'Playwright Tests'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# run 3 copies of the current job in parallel
containers: [1, 2, 3]
container:
image: mcr.microsoft.com/playwright:v1.52.0-jammy
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: actions/cache@v3
id: yarn-and-build-cache
- uses: actions/cache@v4
id: pnpm-and-build-cache
with:
path: |
~/.cache/Cypress
build
node_modules
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node_modules-build-
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
- name: Install pnpm
uses: pnpm/action-setup@v4
# Install NPM dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v3
- uses: actions/setup-node@v4
with:
build: yarn build
start: yarn preview
wait-on: 'http://localhost:3000'
record: true
headless: true
parallel: true
node-version-file: '.node-version'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Run Playwright tests
run: pnpm test:e2e
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CYPRESS_CI: true
CI: true
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
+17 -11
View File
@@ -15,22 +15,28 @@ jobs:
uses: actions/checkout@v3
- uses: actions/cache@v3
id: yarn-and-build-cache
id: pnpm-and-build-cache
with:
path: |
build
node_modules
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node_modules-build-
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Lint & Test
run: |
yarn install
yarn lint
yarn test:unit
- uses: actions/setup-node@v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm lint
- name: Run unit tests
run: pnpm test:unit
+12 -9
View File
@@ -1,14 +1,17 @@
node_modules/
coverage/
.cache/
build/
yarn-error.log
.npmrc
.DS_Store
.cache/
.env.local
.npmrc
/.svelte-kit
/build
/coverage
/docs
/functions
/node_modules
/snapshots.js
/cypress/downloads
/cypress/videos
/cypress/screenshots
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn pre-commit
pnpm pre-commit
+1 -1
View File
@@ -1 +1 @@
20.14.0
22.15.0
+1
View File
@@ -1 +1,2 @@
engine-strict=true
auto-install-peers=true
+2 -1
View File
@@ -5,4 +5,5 @@ build/**
node_modules/**
coverage/**
__snapshots__/**
snapshots.js
snapshots.js
pnpm-lock.yaml
+1 -1
View File
@@ -4,6 +4,6 @@
"bracketSameLine": true,
"trailingComma": "none",
"printWidth": 100,
"tailwindConfig": "./tailwind.config.cjs",
"tailwindConfig": "./tailwind.config.js",
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"]
}
+21 -4
View File
@@ -2,13 +2,15 @@
"editor.formatOnSave": true,
"cSpell.blockCheckingWhenLineLengthGreaterThan": 150,
"cSpell.words": [
"appinstalled",
"asyncable",
"Browserslist",
"ckppp",
"corg",
"cssnano",
"daisyui",
"esserializer",
"fontawesome",
"fsegurai",
"gantt",
"gitgraph",
"KROKI",
@@ -17,17 +19,32 @@
"mindmap",
"Pageview",
"pako",
"panmove",
"panstart",
"panzoom",
"pinchmove",
"pinchstart",
"pzoom",
"roughjs",
"Serde",
"serdes",
"Stackable",
"tailwindcss",
"uparrow"
"uparrow",
"webfonts",
"zenuml"
],
"vitest.commandLine": "yarn test:unit",
"vitest.commandLine": "pnpm test:unit",
"vitest.enable": true,
"testing.autoRun.mode": "rerun",
"svelte.enable-ts-plugin": true,
"githubPullRequests.ignoredPullRequestBranches": ["develop"]
"githubPullRequests.ignoredPullRequestBranches": ["develop"],
"[svelte]": {
"editor.defaultFormatter": "svelte.svelte-vscode"
},
"tailwindCSS.classAttributes": ["class", "className", ".*Classes"],
"tailwindCSS.experimental.classRegex": [
["([\"'`][^\"'`]*.*?[\"'`])", "[\"'`]([^\"'`]*).*?[\"'`]"]
],
"tailwindCSS.emmetCompletions": true
}
+1
View File
@@ -0,0 +1 @@
mermaid.live
+8 -7
View File
@@ -1,16 +1,16 @@
FROM docker.io/library/node:20-alpine3.18 AS mermaid-live-editor-dependencies
FROM docker.io/library/node:22.15.0-alpine3.21 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
RUN corepack enable pnpm
WORKDIR /app
COPY ./package.json .
COPY ./yarn.lock .
COPY ./pnpm-lock.yaml .
RUN yarn install
RUN pnpm install
FROM mermaid-live-editor-dependencies AS mermaid-live-editor-builder
@@ -18,16 +18,17 @@ ARG MERMAID_RENDERER_URL
ARG MERMAID_KROKI_RENDERER_URL
ARG MERMAID_ANALYTICS_URL
ARG MERMAID_DOMAIN
ARG MERMAID_IS_ENABLED_MERMAID_CHART_LINKS
COPY . ./
RUN yarn build
RUN pnpm build
FROM mermaid-live-editor-builder AS mermaid-dev
ENTRYPOINT ["yarn", "dev"]
ENTRYPOINT ["pnpm", "dev"]
FROM nginx:1.25-alpine3.18 AS mermaid
FROM nginx:1.28-alpine3.21 AS mermaid
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=mermaid-live-editor-builder /app/docs /usr/share/nginx/html
+32 -15
View File
@@ -1,10 +1,7 @@
[![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)
[![Join our Discord!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=discord&label=discord)](https://discord.gg/sKeNQX4Wtj)
[![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!
If you want to speed up the progress for mermaid-live-editor, join the slack channel and contact knsv.
# mermaid-live-editor
# Mermaid Live Editor
Edit, preview and share mermaid charts/diagrams.
@@ -17,7 +14,11 @@ Edit, preview and share mermaid charts/diagrams.
## Live demo
You can try out a live version [here](https://mermaid.live/).
You can try out a [live version](https://mermaid.live/).
# Contributors are welcome!
If you want to speed up the progress for mermaid-live-editor, join the Discord channel and contact knsv.
## Docker
@@ -29,13 +30,18 @@ docker run --platform linux/amd64 --publish 8000:8080 ghcr.io/mermaid-js/mermaid
### To configure renderer URL
When building set the MERMAID_RENDERER_URL build argument to the rendering service.
Default is `https://mermaid.ink`
When building set the MERMAID_RENDERER_URL build argument to the rendering
service.
Example:
Default is`https://mermaid.ink`.
Set to empty string to disable PNG and SVG links under Actions
### To configure Kroki Instance URL
When building set the MERMAID_KROKI_RENDERER_URL build argument to your Kroki instance.
When building set the MERMAID_KROKI_RENDERER_URL build argument to your Kroki
instance.
Default is `https://kroki.io`
Set to empty string to disable Kroki link under Actions
### To configure Analytics
@@ -43,6 +49,18 @@ When building set the MERMAID_ANALYTICS_URL build argument to your plausible ins
Default is empty, disabling analytics.
### To enable Mermaid Chart links and promotion
When building set the MERMAID_IS_ENABLED_MERMAID_CHART_LINKS build argument to `true`
Default is empty, disabling button to save to Mermaid Chart and promotional banner.
### To update the Security modal
The modal shown on clicking the security link assumes analytics, renderer, Kroki
and Mermaid chart are enabled. You can update it by modifying `Privacy.svelte`
if you wish.
### Development
```bash
@@ -81,15 +99,14 @@ https://docs.github.com/en/get-started/quickstart/fork-a-repo
## 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`
- [Node.js](https://nodejs.org/en/) current LTS version
- [pnpm](https://pnpm.io/) package manager. Install with `corepack enable pnpm`
## Development
```sh
yarn install
yarn dev -- --open
pnpm install
pnpm dev -- --open
```
This app is created with Svelte Kit.
+1 -1
View File
@@ -6,7 +6,7 @@
# git clone https://github.com/mermaid-js/docs.git
set -e
rm -rf docs
yarn release
pnpm release
pushd .
if [ ! -d ../docs ]; then
echo "Clone https://github.com/mermaid-js/docs to parent folder before continuing."
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://next.shadcn-svelte.com/schema.json",
"style": "new-york",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app.postcss",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
},
"typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
}
-37
View File
@@ -1,37 +0,0 @@
import { defineConfig } from 'cypress';
import fs from 'fs';
import path from 'path';
export default defineConfig({
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'
}
});
-10
View File
@@ -1,10 +0,0 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": "off"
},
"env": {
"cypress/globals": true
}
}
-45
View File
@@ -1,45 +0,0 @@
import { typeInEditor, verifyFileSizeGreaterThan } from './util';
describe('Check actions', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('Actions').click();
});
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.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.get(`#downloadPNG`).click();
verifyFileSizeGreaterThan('diagram', 'png', 34_000);
cy.get(`#downloadSVG`).click();
verifyFileSizeGreaterThan('diagram', 'svg', 10_000);
// Verify downloaded file is different for different diagrams
cy.contains('Sample Diagrams').click();
cy.contains('ER').click();
cy.get(`#downloadPNG`).click();
verifyFileSizeGreaterThan('diagram', 'png', 40_000);
cy.get(`#downloadSVG`).click();
verifyFileSizeGreaterThan('diagram', 'svg', 11_000);
});
});
-115
View File
@@ -1,115 +0,0 @@
import { typeInEditor, cmd } from './util';
describe('Auto sync tests', () => {
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('#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');
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 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('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');
});
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');
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('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');
});
});
-36
View File
@@ -1,36 +0,0 @@
describe('Editor docs tests', () => {
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.org/"]`).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$="/syntax/flowchart.html#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$="/syntax/sequenceDiagram.html"]`).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$="/syntax/stateDiagram.html"]`).should('exist');
});
});
-103
View File
@@ -1,103 +0,0 @@
import { typeInEditor, verifyFileSnapshot } from './util';
describe('Save History', () => {
beforeEach(() => {
cy.clock(new Date(2022, 0, 1).getTime());
cy.clearLocalStorage();
cy.visit('/edit');
cy.contains('History').click();
});
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('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');
});
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();
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', () => {
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)');
});
});
-136
View File
@@ -1,136 +0,0 @@
import { toBase64 } from 'js-base64';
describe('Site Loads', () => {
beforeEach(() => {
cy.clearLocalStorage();
cy.visit('/');
cy.url().should('include', '/edit#pako');
});
it('Check Home page load', () => {
cy.url().should('include', '/edit');
cy.contains('History').click();
cy.getLocalStorage('codeStore').snapshot();
});
it('should keep code after reload', () => {
cy.get('#editor').contains('Car');
cy.reload();
cy.get('#editor').contains('Car');
});
it('Check Redirect from old URL', () => {
cy.visit(
'/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
);
cy.url().should('include', '/edit#pako:eNp');
});
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');
});
(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();
});
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 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();
});
});
// 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.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 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');
});
});
});
-56
View File
@@ -1,56 +0,0 @@
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('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 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('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();
});
});
});
-55
View File
@@ -1,55 +0,0 @@
export const cmd = `{${Cypress.platform === 'darwin' ? 'meta' : 'ctrl'}}`;
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 verifyFileSizeGreaterThan = (
fileType: 'history' | 'diagram',
extension: string,
size: number
) => {
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
) => {
cy.task('readAndDeleteFile', {
folder: downloadsFolder,
fileNamePattern: `^mermaid-${fileType}-.*.${extension}$`,
mode: 'content'
}).then((fileContent: number) => {
expect(fileContent).to.contain(content);
});
};
-5
View File
@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
-52
View File
@@ -1,52 +0,0 @@
module.exports = {
"Site Loads": {
"Check Home page load": {
"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}"
},
"should load diagram from gist": {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a\"}}}"
},
"should load diagram from gist revision": {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello\\\"\\n}\",\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"gist\",\"config\":{\"url\":\"https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/ec9b4ab0e41e4ff6287326cd3cb47affd7851e19\"}}}"
},
"should load diagram from raw files": {
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"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": "12.17.4",
"Auto sync tests": {
"should dim diagram when code is edited": {
"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\":\"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": {
"should set light theme as default": {
"1": "{\"theme\":\"light\",\"isDark\":false}"
},
"should change themes when clicked": {
"1": "{\"theme\":\"cupcake\",\"isDark\":false}"
},
"should set dark theme as default": {
"1": "{\"theme\":\"dark\",\"isDark\":true}"
},
"Test light themes": {
"should set light theme as default": {
"1": "{\"theme\":\"light\",\"isDark\":false}"
},
"should change themes when clicked": {
"1": "{\"theme\":\"cupcake\",\"isDark\":false}"
}
},
"Test dark mode": {
"should set dark theme as default": {
"1": "{\"theme\":\"dark\",\"isDark\":true}"
}
}
}
}
-38
View File
@@ -1,38 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
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;
}
}
}
import 'cypress-localstorage-commands';
-27
View File
@@ -1,27 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// 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;
}
});
-7
View File
@@ -1,7 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"types": ["cypress", "cypress-localstorage-commands", "node"]
},
"include": ["**/*.ts"]
}
+9
View File
@@ -1,3 +1,12 @@
[build.environment]
MERMAID_ANALYTICS_URL = 'https://p.mermaid.live'
MERMAID_DOMAIN = 'mermaid.live'
MERMAID_RENDERER_URL = 'https://mermaid.ink'
MERMAID_KROKI_RENDERER_URL = 'https://kroki.io'
MERMAID_IS_ENABLED_MERMAID_CHART_LINKS ='true'
[[redirects]]
from = "/index.html"
to = "/edit"
status = 301
force = true
+82 -51
View File
@@ -5,8 +5,8 @@
"license": "MIT",
"scripts": {
"dev": "vite dev",
"dev:force": "MERMAID_LOCAL=true yarn dev --force",
"dev:test": "yarn dev",
"dev:force": "MERMAID_LOCAL=true pnpm dev --force",
"dev:test": "pnpm dev",
"build": "vite build",
"preview": "vite preview",
"lint": "prettier --check --cache . && eslint --ignore-path .gitignore .",
@@ -15,73 +15,97 @@
"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"
"test:unit:ui": "vitest --ui",
"test:unit:coverage": "vitest run --coverage",
"test": "pnpm test:unit && pnpm test:e2e",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug"
},
"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",
"@fortawesome/fontawesome-free": "^6.7.2",
"@iconify-json/material-symbols": "^1.2.20",
"@iconify-json/mdi": "^1.2.3",
"@playwright/test": "^1.52.0",
"@sveltejs/adapter-static": "3.0.8",
"@sveltejs/kit": "2.20.8",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"@types/hammerjs": "^2.0.46",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.15.10",
"@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",
"@vitest/coverage-v8": "2.1.9",
"@vitest/ui": "^2.1.9",
"autoprefixer": "^10.4.21",
"bits-ui": "^1.4.6",
"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",
"chai": "^4.5.0",
"clsx": "^2.1.1",
"cssnano": "^6.1.2",
"eslint": "8.57.1",
"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-no-only-tests": "^3.3.0",
"eslint-plugin-postcss-modules": "^2.0.0",
"eslint-plugin-svelte": "^2.35.1",
"eslint-plugin-tailwindcss": "^3.13.1",
"eslint-plugin-sort-keys": "^2.3.5",
"eslint-plugin-svelte": "^2.46.1",
"eslint-plugin-tailwindcss": "^3.18.0",
"eslint-plugin-unicorn": "^50.0.1",
"eslint-plugin-vitest": "^0.5.0",
"eslint-plugin-vitest": "^0.5.4",
"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",
"jsdom": "^25.0.1",
"lint-staged": "^15.5.1",
"lucide-svelte": "^0.507.0",
"node-html-parser": "^6.1.13",
"paneforge": "^1.0.0-next.5",
"postcss": "^8.5.3",
"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",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.28.2",
"svelte-preprocess": "^6.0.3",
"svelte-sonner": "^0.3.28",
"tailwind-merge": "^3.2.0",
"tailwind-variants": "^0.3.1",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"tslib": "^2.8.1",
"typescript": "^5.8.3",
"unplugin-icons": "^22.1.0",
"vite": "^5.4.19",
"vitest": "^2.1.9",
"vitest-dom": "^0.1.1"
},
"dependencies": {
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-markdown": "^6.3.2",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.0",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.36.7",
"@fontsource-variable/recursive": "^5.2.5",
"@fsegurai/codemirror-theme-vscode-dark": "^6.1.4",
"@fsegurai/codemirror-theme-vscode-light": "^6.1.4",
"@mermaid-js/layout-elk": "^0.1.7",
"@mermaid-js/mermaid-zenuml": "^0.2.0",
"daisyui": "2.52.0",
"dayjs": "^1.11.7",
"codemirror": "^6.0.1",
"dayjs": "^1.11.13",
"hammerjs": "^2.0.8",
"js-base64": "3.7.7",
"lodash-es": "^4.17.21",
"mermaid": "10.9.1",
"monaco-editor": "0.49.0",
"mermaid": "^11.6.0",
"mode-watcher": "^0.5.1",
"monaco-editor": "0.52.2",
"pako": "2.1.0",
"plausible-tracker": "^0.3.8",
"plausible-tracker": "^0.3.9",
"random-word-slugs": "0.1.7",
"svg-pan-zoom": "3.6.1",
"svg2roughjs": "^3.2.0",
"svg-pan-zoom": "3.6.2",
"svg2roughjs": "^3.2.1",
"uuid": "9.0.1"
},
"lint-staged": {
@@ -90,11 +114,18 @@
"eslint --ignore-path .gitignore "
]
},
"volta": {
"node": "18.20.3",
"yarn": "1.22.22"
},
"engines": {
"node": ">=16.7"
"node": ">=20.19.0"
},
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
"pnpm": {
"onlyBuiltDependencies": [
"deasync",
"esbuild",
"svelte-preprocess"
],
"ignoredBuiltDependencies": [
"vue-demi"
]
}
}
+28
View File
@@ -0,0 +1,28 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
forbidOnly: !!process.env.CI,
fullyParallel: true,
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
}
],
reporter: process.env.CI ? [['github'], ['list']] : 'list',
retries: process.env.CI ? 2 : 0,
testDir: './tests',
use: {
baseURL: 'http://localhost:3000',
browserName: 'chromium',
permissions: ['clipboard-read', 'clipboard-write'],
trace: 'retain-on-failure',
viewport: { width: 1920, height: 1080 }
},
webServer: {
command: `pnpm ${process.env.CI ? 'preview' : 'dev'}`,
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI
},
workers: process.env.CI ? 3 : undefined
});
+7647
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -24,5 +24,6 @@
"major": {
"dependencyDashboardApproval": true
},
"dependencyDashboardAutoclose": true
"dependencyDashboardAutoclose": true,
"rangeStrategy": "bump"
}
+3 -5
View File
@@ -4,20 +4,18 @@
<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" />
<meta name="og:image" content="%sveltekit.assets%/favicon.svg" />
<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" />
<meta name="theme-color" content="#ff3670" />
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
%sveltekit.head%
</head>
<body style="overflow: hidden">
<body>
<div id="svelte">%sveltekit.body%</div>
</body>
</html>
+60 -9
View File
@@ -1,17 +1,68 @@
@import '@fortawesome/fontawesome-free/css/all.min.css';
@import '@fontsource-variable/recursive/crsv.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
.input {
@apply flex-1 rounded border-2 border-solid border-primary;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 240 10% 91%;
--primary-foreground: 255 20% 15%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 228 24% 96%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 340 100% 44%;
--accent-foreground: 210 40% 98%;
--destructive: 0 72.22% 50.59%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.75rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--primary: 210 40% 30%;
--primary-foreground: 222.2 47.4% 90%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--accent: 340 100% 44%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 212.7 26.8% 83.9%;
}
}
.action-btn {
@apply btn btn-primary;
@layer base {
* {
@apply border-border;
}
body {
@apply h-screen w-screen overflow-hidden bg-background text-foreground;
}
}
#graph-div sub,
#graph-div sup {
position: initial;
vertical-align: revert;
body {
font-family: 'Recursive Variable', sans-serif;
}
.d {
@apply border border-red-500;
}
+1
View File
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
readonly MERMAID_KROKI_RENDERER_URL?: string;
readonly MERMAID_ANALYTICS_URL?: string;
readonly MERMAID_DOMAIN?: string;
readonly MERMAID_IS_ENABLED_MERMAID_CHART_LINKS?: string;
// more env variables...
}
+226 -154
View File
@@ -1,84 +1,141 @@
<script lang="ts">
import Card from '$/components/Card/Card.svelte';
import CopyButton from '$/components/CopyButton.svelte';
import CopyInput from '$/components/CopyInput.svelte';
import { Button } from '$/components/ui/button';
import { Input } from '$/components/ui/input';
import { Separator } from '$/components/ui/separator';
import * as ToggleGroup from '$/components/ui/toggle-group';
import { TID } from '$/constants';
import { env } from '$/util/env';
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 { inputStateStore, stateStore, urlsStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';
import dayjs from 'dayjs';
import { toBase64 } from 'js-base64';
import { version as FAVersion } from '@fortawesome/fontawesome-free/package.json';
import DownloadIcon from '~icons/material-symbols/download';
import ExternalLinkIcon from '~icons/material-symbols/open-in-new-rounded';
import WidthIcon from '~icons/material-symbols/width-rounded';
const FONT_AWESOME_URL = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}/css/all.min.css`;
const fontAwesomeURLs = (() => {
const baseUrl = `https://cdnjs.cloudflare.com/ajax/libs/font-awesome/${FAVersion}`;
return {
css: `${baseUrl}/css/all.min.css`,
woff2: `${baseUrl}/webfonts/fa-solid-900.woff2`
};
})();
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;
const getSvgElement = async () => {
try {
$inputStateStore.panZoom = false;
await new Promise((resolve) => setTimeout(resolve, 1000));
await waitForRender();
const svgElement = document.querySelector('#container svg');
if (!svgElement) {
throw new Error('svg not found');
}
svgElement.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
return {
svg: svgElement.cloneNode(true) as HTMLElement,
box: svgElement.querySelector('g')!.getBoundingClientRect()
};
} finally {
setTimeout(() => {
$inputStateStore.panZoom = true;
}, 10000);
}
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 injectFontAwesome = async ({
svgString,
fontAwesomeEmbedMode
}: {
svgString: string;
fontAwesomeEmbedMode: 'raw' | 'url';
}) => {
if (fontAwesomeEmbedMode === 'url') {
return `<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="${fontAwesomeURLs.css}" type="text/css"?>
${svgString}`;
}
const [fontAwesomeCSS, fontBlob] = await Promise.all([
fetch(fontAwesomeURLs.css).then((response) => response.text()),
fetch(fontAwesomeURLs.woff2).then((res) => res.blob())
]);
const reader = new FileReader();
const fontBase64 = await new Promise<string>((resolve) => {
reader.onloadend = () => {
const base64 = reader.result as string;
// Remove the data URL prefix (data:application/octet-stream;base64,)
resolve(base64.split(',')[1]);
};
reader.readAsDataURL(fontBlob);
});
const styleString = `
@font-face {
font-family: 'Font Awesome 6 Free';
font-style: normal;
font-weight: 900;
src: url(data:font/woff2;base64,${fontBase64}) format('woff2');
}
.fa {
font-family: 'Font Awesome 6 Free';
font-weight: 900;
// TODO: Make this dynamic from config
font-size: 16px;
width: 16px;
fill: black;
}
${fontAwesomeCSS}
`;
return svgString.replace('<style>', `<style>${styleString}`);
};
const getBase64SVG = async ({
svg,
box,
width,
height,
embedFontAwesome
}: {
svg?: HTMLElement;
box?: DOMRect;
width?: number;
height?: number;
embedFontAwesome?: boolean;
} = {}): Promise<string> => {
if (!svg || !box) {
({ svg, box } = await getSvgElement());
}
console.log(box);
// 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
svg.setAttribute('viewBox', `0 0 ${box.width} ${box.height}`);
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 svgStringWithFontAwesome = await injectFontAwesome({
svgString,
fontAwesomeEmbedMode: embedFontAwesome ? 'raw' : 'url'
});
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;
console.log(svgStringWithFontAwesome);
return toBase64(svgStringWithFontAwesome);
};
const simulateDownload = (download: string, href: string): void => {
@@ -88,6 +145,48 @@ ${svgString}`);
a.click();
a.remove();
};
const exportImage = async (event: Event, exporter: Exporter) => {
event.stopPropagation();
event.preventDefault();
const canvas = document.createElement('canvas');
const { svg, box } = await getSvgElement();
if (imageSizeMode === 'width') {
const ratio = box.height / box.width;
canvas.width = imageSize;
canvas.height = imageSize * ratio;
} else if (imageSizeMode === 'height') {
const ratio = box.width / box.height;
canvas.width = imageSize * ratio;
canvas.height = imageSize;
} else {
const multiplier = 2;
canvas.width = box.width * multiplier;
canvas.height = box.height * multiplier;
}
const context = canvas.getContext('2d');
if (!context) {
throw new Error('context not found');
}
context.fillStyle = `hsl(${window.getComputedStyle(document.body).getPropertyValue('--background')})`;
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,${await getBase64SVG({ svg, box, height: canvas.height, width: canvas.width, embedFontAwesome: true })}`;
// Fallback to set panZoom to true after 2 seconds
// This is a workaround for the case when the image is not loaded
// setTimeout(() => {
// if (!$inputStateStore.panZoom) {
// }
// }, 2000);
};
const downloadImage: Exporter = (context, image) => {
return () => {
const { canvas } = context;
@@ -100,7 +199,7 @@ ${svgString}`);
};
const isClipboardAvailable = (): boolean => {
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem') as boolean;
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem');
};
const clipboardCopy: Exporter = (context, image) => {
@@ -136,127 +235,100 @@ ${svgString}`);
});
};
const onDownloadSVG = () => {
simulateDownload(getFileName('svg'), `data:image/svg+xml;base64,${getBase64SVG()}`);
const onDownloadSVG = async () => {
simulateDownload(getFileName('svg'), `data:image/svg+xml;base64,${await getBase64SVG()}`);
logEvent('download', {
type: 'svg'
});
};
const onCopyMarkdown = () => {
document.querySelector<HTMLInputElement>('#markdown')?.select();
document.execCommand('Copy');
logEvent('copyMarkdown');
};
let gistURL = '';
let gistURL = $state('');
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');
return 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 imageSizeMode: 'auto' | 'width' | 'height' = $state('auto');
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})`;
$effect(() => {
if (!imageSizeMode) {
imageSizeMode = 'auto';
}
});
let imageSize = $state(1080);
const isNetlify = browser && window.location.host.includes('netlify');
</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>
{#snippet dualActionButton(text: string, download: (event: Event) => unknown, url?: string)}
<div class="flex flex-grow gap-0.5">
<Button
class={['flex-grow', url && 'rounded-r-none']}
onclick={download}
data-testid="download-{text}">
<DownloadIcon />
{text}
</Button>
{#if url}
<Button class="rounded-l-none" href={url} target="_blank" rel="noreferrer noopener">
<ExternalLinkIcon />
</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>
{/snippet}
<div class="flex items-center gap-2">
<Card title="Actions" isStackable icon={{ component: DownloadIcon, class: 'rotate-180' }}>
<div class="flex min-w-fit flex-col gap-2 p-2">
<div class="flex w-full items-center gap-2 whitespace-nowrap py-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} />
<ToggleGroup.Root type="single" variant="outline" bind:value={imageSizeMode}>
<ToggleGroup.Item value="auto">Auto</ToggleGroup.Item>
<ToggleGroup.Item value="width">Width</ToggleGroup.Item>
<ToggleGroup.Item value="height">Height</ToggleGroup.Item>
</ToggleGroup.Root>
{#if imageSizeMode !== 'auto'}
<WidthIcon
class={['size-6 shrink-0 transition-all', imageSizeMode === 'width' && 'rotate-90']} />
{/if}
<Input
type="number"
min="3"
max="10000"
disabled={imageSizeMode === 'auto'}
bind:value={imageSize} />
</div>
<div class="flex gap-2">
{@render dualActionButton('PNG', onDownloadPNG, $urlsStore.png)}
{@render dualActionButton('SVG', onDownloadSVG, $urlsStore.svg)}
{#if env.krokiRendererUrl}
<a target="_blank" rel="noreferrer" class="flex-grow" href={$urlsStore.kroki}>
<Button class="action-btn flex w-full items-center gap-2">
<ExternalLinkIcon /> Kroki
</Button>
</a>
{/if}
</div>
<Separator />
{#if isClipboardAvailable()}
<CopyButton onclick={onCopyClipboard} label="Copy Image" />
{/if}
{#if $urlsStore.mdCode}
<CopyInput value={$urlsStore.mdCode} label="Copy Markdown" testID={TID.copyMarkdown} />
{/if}
<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>
<Input type="url" bind:value={gistURL} placeholder="Enter Gist URL" />
<Button onclick={loadGist}>Load Gist</Button>
</div>
{#if isNetlify}
<div class="flex w-full items-center justify-center">
+72 -20
View File
@@ -1,33 +1,85 @@
<script lang="ts">
import type { Tab } from '$lib/types';
import type { Tab } from '$/types';
import type { Component, Snippet } from 'svelte';
import { quintOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
import CollapseAllIcon from '~icons/material-symbols/collapse-all-rounded';
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;
interface Props {
isClosable?: boolean;
isOpen?: boolean;
isStackable?: boolean;
tabs?: Tab[];
activeTabID?: string;
title?: string;
icon?: {
component: Component;
class?: string;
};
onselect?: (tab: Tab) => void;
actions?: Snippet;
children: Snippet;
}
let {
isClosable = true,
isOpen = false,
isStackable = false,
tabs = [],
activeTabID = '',
title,
icon,
onselect,
actions,
children
}: Props = $props();
const toggleCardOpen = () => {
if (isClosable) {
isOpen = !isOpen;
}
};
let isTabsShown = $derived(isOpen && tabs.length > 0);
</script>
<div class="card m-2 flex flex-grow flex-col overflow-hidden rounded shadow-2xl">
<div
class={[
'card flex h-fit flex-col overflow-hidden rounded-2xl border-2 border-muted',
isOpen && 'isOpen flex-grow',
isStackable ? 'flex-1 group-has-[.isOpen]:w-full group-has-[.isOpen]:flex-none' : 'w-full'
]}>
<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>
class={[
'flex h-11 flex-none cursor-pointer items-center justify-between whitespace-nowrap bg-muted p-2',
isTabsShown && 'pb-1'
]}
onclick={toggleCardOpen}
onkeypress={toggleCardOpen}>
{#if icon || title}
<span role="menubar" tabindex="0" class="flex w-fit items-center gap-3">
{#if icon}
<icon.component class={icon.class} />
{/if}
{title}
</span>
{/if}
{#if isOpen && tabs && tabs.length > 0}
<Tabs {onselect} {tabs} {activeTabID} />
{/if}
{@render actions?.()}
{#if isOpen && isClosable}
<CollapseAllIcon />
{/if}
</div>
{#if isOpen}
<div class="card-body flex-grow overflow-auto p-0 text-base-content" transition:slide>
<slot />
<div class="flex-grow overflow-x-auto" transition:slide={{ easing: quintOut }}>
{@render children()}
</div>
{/if}
</div>
+41 -45
View File
@@ -1,56 +1,52 @@
<script lang="ts">
import type { Tab, TabEvents } from '$lib/types';
import { createEventDispatcher } from 'svelte';
import { Button } from '$/components/ui/button';
import { Separator } from '$/components/ui/separator';
import type { Tab } from '$lib/types';
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;
let {
tabs,
activeTabID,
onselect
}: {
tabs: Tab[];
activeTabID: string;
onselect?: (tab: Tab) => void;
} = $props();
if (!activeTabID && tabs.length > 0) {
activeTabID = tabs[0].id;
}
const dispatch = createEventDispatcher<TabEvents>();
const toggleTabs = (tab: Tab) => {
activeTabID = tab.id;
dispatch('select', tab);
return (event: Event) => {
event.stopPropagation();
onselect?.(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>
<div class="flex w-fit cursor-default items-center gap-2">
<ul class="flex gap-2 align-middle" transition:fade>
{#each tabs as tab, index}
<Button
role="tab"
variant="ghost"
class={[
'px-2',
activeTabID === tab.id && 'rounded-b-none border-b-2 border-b-primary-foreground/50'
]}
onclick={toggleTabs(tab)}
onkeypress={toggleTabs(tab)}>
<tab.icon />
{tab.title}
</Button>
<style>
.icon {
transition-duration: 0.5s;
}
.isOpen {
transform: rotate(90deg);
}
</style>
{#if index < tabs.length - 1}
<div class="my-2">
<Separator orientation="vertical" class="w-0.5 bg-slate-300" />
</div>
{/if}
{/each}
</ul>
</div>
-23
View File
@@ -1,23 +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', 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');
});
});
+41
View File
@@ -0,0 +1,41 @@
<script lang="ts">
import { Button } from '$/components/ui/button';
import type { InputType } from '$/types';
import { notify } from '$/util/notify';
import { scale } from 'svelte/transition';
import CheckIcon from '~icons/material-symbols/check-rounded';
import CopyIcon from '~icons/material-symbols/content-copy-outline-rounded';
let {
onclick,
label = 'Copy'
}: { onclick: (event?: Event) => Promise<unknown>; label?: string; type?: InputType } = $props();
let showCheckIcon = $state(false);
</script>
<Button
onclick={async (event) => {
try {
showCheckIcon = true;
setTimeout(() => {
showCheckIcon = false;
}, 1000);
await onclick(event);
} catch {
notify('Failed to copy');
}
}}>
<div class="grid">
{#key showCheckIcon}
<span transition:scale class="col-start-1 row-start-1">
{#if showCheckIcon}
<CheckIcon />
{:else}
<CopyIcon />
{/if}
</span>
{/key}
</div>
{label}
</Button>
+25
View File
@@ -0,0 +1,25 @@
<script lang="ts">
import CopyButton from '$/components/CopyButton.svelte';
import { Input } from '$/components/ui/input';
import type { InputType } from '$/types';
import { copyToClipboard } from '$/util/util';
let {
value,
label = 'Copy',
type = 'url',
testID
}: { value: string; label?: string; type?: InputType; testID?: string } = $props();
</script>
<div class="flex w-full items-center gap-2">
<Input
{type}
{value}
data-testid={testID}
onclick={(event) => {
event.currentTarget.setSelectionRange(0, event.currentTarget.value.length);
}} />
<CopyButton onclick={() => copyToClipboard(value)} {label} />
</div>
+118
View File
@@ -0,0 +1,118 @@
<script lang="ts">
import type { EditorProps } from '$/types';
import { stateStore } from '$/util/state';
import { initEditor } from '$lib/util/monacoExtra';
import { errorDebug } from '$lib/util/util';
import { mode } from 'mode-watcher';
import * as monaco from 'monaco-editor';
import monacoEditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import monacoJsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import { onMount } from 'svelte';
const { onUpdate }: EditorProps = $props();
let divElement: HTMLDivElement | undefined = $state();
let editor: monaco.editor.IStandaloneCodeEditor | undefined;
let editorOptions = {
minimap: {
enabled: false
},
overviewRulerLanes: 0
} satisfies monaco.editor.IStandaloneEditorConstructionOptions;
let currentText = '';
const jsonModel = monaco.editor.createModel(
'',
'json',
monaco.Uri.parse('internal://config.json')
);
const mermaidModel = monaco.editor.createModel(
'',
'mermaid',
monaco.Uri.parse('internal://mermaid.mmd')
);
onMount(() => {
self.MonacoEnvironment = {
getWorker(_, label) {
if (label === 'json') {
return new monacoJsonWorker();
}
return new monacoEditorWorker();
}
};
if (!divElement) {
throw new Error('divEl is undefined');
}
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
enableSchemaRequest: true,
schemas: [
{
fileMatch: ['config.json'],
uri: 'https://mermaid.js.org/schemas/config.schema.json'
}
]
});
initEditor(monaco);
errorDebug();
editor = monaco.editor.create(divElement, editorOptions);
editor.onDidChangeModelContent(({ isFlush }) => {
const newText = editor?.getValue();
if (!newText || currentText === newText || isFlush) {
return;
}
currentText = newText;
onUpdate(currentText);
});
const unsubscribeState = stateStore.subscribe(({ errorMarkers, editorMode, code, mermaid }) => {
if (!editor) {
return;
}
const model = editorMode === 'code' ? mermaidModel : jsonModel;
if (editor.getModel()?.id !== model.id) {
editor.setModel(model);
}
// Update editor text if it's different
const newText = editorMode === 'code' ? code : mermaid;
if (newText !== currentText) {
editor.setScrollTop(0);
editor.setValue(newText);
currentText = newText;
}
// Display/clear errors
monaco.editor.setModelMarkers(model, 'mermaid', errorMarkers);
});
const unsubscribeMode = mode.subscribe((mode) => {
editor && monaco.editor.setTheme(`mermaid${mode === 'dark' ? '-dark' : ''}`);
});
const resizeObserver = new ResizeObserver((entries) => {
editor?.layout({
height: entries[0].contentRect.height,
width: entries[0].contentRect.width
});
});
if (divElement.parentElement) {
resizeObserver.observe(divElement);
}
return () => {
unsubscribeState();
unsubscribeMode();
resizeObserver.disconnect();
editor?.dispose();
};
});
</script>
<div bind:this={divElement} id="editor" class="h-full flex-grow overflow-hidden"></div>
@@ -0,0 +1,109 @@
<script lang="ts">
import { Button } from '$/components/ui/button';
import { TID } from '$/constants';
import type { DocumentationConfig } from '$/types';
import { standardizeDiagramType } from '$/util/mermaid';
import { stateStore } from '$/util/state';
import BookIcon from '~icons/material-symbols/book-2-outline-rounded';
const docURLBase = 'https://mermaid.js.org';
const docMap = {
architecture: {
code: '/syntax/architecture.html'
},
block: {
code: '/syntax/block.html'
},
c4: {
code: '/syntax/c4.html'
},
class: {
code: '/syntax/classDiagram.html',
config: '/syntax/classDiagram.html#configuration'
},
er: {
code: '/syntax/entityRelationshipDiagram.html',
config: '/syntax/entityRelationshipDiagram.html#styling'
},
flowchart: {
code: '/syntax/flowchart.html',
config: '/syntax/flowchart.html#configuration'
},
gantt: {
code: '/syntax/gantt.html',
config: '/syntax/gantt.html#configuration'
},
gitGraph: {
code: '/syntax/gitgraph.html',
config: '/syntax/gitgraph.html#gitgraph-specific-configuration-options'
},
journey: {
code: '/syntax/userJourney.html'
},
kanban: {
code: '/syntax/kanban.html',
config: '/syntax/kanban.html#configuration-options'
},
mindmap: {
code: '/syntax/mindmap.html'
},
packet: {
code: '/syntax/packet.html',
config: '/config/schema-docs/config-defs-packet-diagram-config.html'
},
pie: {
code: '/syntax/pie.html',
config: '/syntax/pie.html#configuration'
},
quadrantChart: {
code: '/syntax/quadrantChart.html',
config: '/syntax/quadrantChart.html#chart-configurations'
},
requirement: {
code: '/syntax/requirementDiagram.html'
},
sankey: {
code: '/syntax/sankey.html',
config: '/syntax/sankey.html#configuration'
},
sequence: {
code: '/syntax/sequenceDiagram.html',
config: '/syntax/sequenceDiagram.html#configuration'
},
stateDiagram: {
code: '/syntax/stateDiagram.html'
},
timeline: {
code: '/syntax/timeline.html',
config: '/syntax/timeline.html#themes'
},
xychart: {
code: '/syntax/xyChart.html',
config: '/syntax/xyChart.html#chart-configurations'
},
zenuml: {
code: '/syntax/zenuml.html'
}
} as const satisfies DocumentationConfig;
const doc = $derived.by(() => {
const { editorMode, diagramType } = $stateStore;
if (!diagramType) {
return { key: '', url: docURLBase };
}
const key = standardizeDiagramType(diagramType);
const docConfig = docMap[key] ?? { code: '' };
const url = docURLBase + (docConfig[editorMode] ?? docConfig.code ?? '');
return { key, url };
});
</script>
<Button
variant="ghost"
data-testid={TID.diagramDocumentationButton}
href={doc.url}
target="_blank"
title="View documentation for {doc.key.replace('Diagram', '')} diagram">
<BookIcon />
Docs
</Button>
+38
View File
@@ -0,0 +1,38 @@
<script lang="ts">
import { Button } from '$/components/ui/button';
import * as Popover from '$/components/ui/popover';
import type { Component } from 'svelte';
interface Props {
links: { title: string; href: string }[];
icon?: Component;
class?: string;
}
let props: Props = $props();
</script>
<Popover.Root>
<Popover.Trigger class="flex items-center gap-0">
<Button variant="ghost" size="sm">
<props.icon class={props.class} />
</Button>
</Popover.Trigger>
<Popover.Content>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<ul tabindex="0" class="flex flex-col">
{#each props.links as { href, title }}
<li class="rounded-md p-2 hover:bg-muted">
<a
role="menuitem"
tabindex="0"
class="whitespace-nowrap underline"
target="_blank"
{href}>
{title}
</a>
</li>
{/each}
</ul>
</Popover.Content>
</Popover.Root>
+46 -124
View File
@@ -1,133 +1,55 @@
<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';
import DesktopEditor from '$/components/DesktopEditor.svelte';
import McWrapper from '$/components/McWrapper.svelte';
import MermaidChartIcon from '$/components/MermaidChartIcon.svelte';
import MobileEditor from '$/components/MobileEditor.svelte';
import { Button } from '$/components/ui/button';
import { TID } from '$/constants';
import { env } from '$/util/env';
import { stateStore, updateCode, updateConfig, urlsStore } from '$lib/util/state';
import ExclamationCircleIcon from '~icons/material-symbols/error-outline-rounded';
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') {
let { isMobile }: { isMobile: boolean } = $props();
const onUpdate = (text: string) => {
if ($stateStore.editorMode === '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" />
<div class="flex h-full flex-col">
{#if isMobile}
<MobileEditor {onUpdate} />
{:else}
<DesktopEditor {onUpdate} />
{/if}
{#if $stateStore.error instanceof Error}
<div class="flex flex-col text-sm" data-testid={TID.errorContainer}>
<div class="flex items-center justify-between gap-2 bg-slate-900 p-2 text-white">
<div class="flex w-fit items-center gap-2">
<ExclamationCircleIcon class="size-6 text-destructive" aria-hidden="true" />
<div class="flex flex-col">
<p>Syntax error</p>
{#if env.isEnabledMermaidChartLinks}
<p class="text-xs text-white/60">Create a free account to repair with AI</p>
{/if}
</div>
</div>
<McWrapper>
<Button
variant="accent"
size="sm"
href={$urlsStore.mermaidChart({ medium: 'ai_repair' }).save}>
<MermaidChartIcon />
AI Repair
</Button>
</McWrapper>
</div>
<output class="max-h-32 overflow-auto bg-muted p-2" name="mermaid-error" for="editor">
<pre>{$stateStore.error?.toString()}</pre>
</output>
</div>
{/if}
</div>
+13
View File
@@ -0,0 +1,13 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
children: Snippet;
}
let { children }: Props = $props();
</script>
<div class="flex h-12 items-center justify-between gap-2 rounded-2xl bg-muted p-3">
{@render children()}
</div>
+54
View File
@@ -0,0 +1,54 @@
<!--
@component
Exports a `waitForFontAwesomeToLoad` function that waits for FontAwesome to load.
Usage:
```svelte
<script lang="ts">
import FontAwesome from '$lib/client/components/FontAwesome';
let waitForFontAwesomeToLoad: FontAwesome["waitForFontAwesomeToLoad"];
</script>
<FontAwesome bind:waitForFontAwesomeToLoad />
```
-->
<script context="module" lang="ts">
/**
* Returns `true` if the code may contain FontAwesome icons, and we should
* wait for the fonts to load before rendering.
*/
export function mayContainFontAwesome(code: string) {
// taken from https://github.com/mermaid-js/mermaid/blob/7043892e871d0c413ec63dc1570a8ef738d15568/packages/mermaid/src/diagrams/flowchart/flowRenderer-v2.js#L63
// Not ideal, since we're looking at unparsed code.
const regex = /fa[blrs]?:fa-[\w-]+/g;
return regex.test(code);
}
</script>
<script lang="ts">
// Vite will automatically take care of adding this to our `<head>`
let lazyLoadFontAwesomeCSS = import('./FontAwesomeCSS.svelte');
// eslint-disable-next-line unicorn/prefer-top-level-await
let fontsLoaded = (async () => {
await lazyLoadFontAwesomeCSS;
// Once the stylesheet has been parsed, the fonts will be in document.fonts
// TODO: Maybe we should scan the CSS style sheet only, and only load FontAwesome fonts
await Promise.allSettled(Array.from(document.fonts, (font) => font.load()));
})();
/**
* Wait for FontAwesome to load.
*
* @returns A promise that resolves when FontAwesome is
* loaded, or there was an error that we ignore.
*/
export async function waitForFontAwesomeToLoad() {
return await fontsLoaded;
}
</script>
{#await lazyLoadFontAwesomeCSS then FontAwesomeCSS}
<FontAwesomeCSS.default />
{/await}
+14
View File
@@ -0,0 +1,14 @@
<!--
@component
Imports the FontAwesome CSS file.
Needed, since the `@sveltejs/adapter-node` plugin doesn't seem to support
lazy-importing CSS, even though `@sveltejs/adapter-vercel` is fine with it!
However, making a `.svelte` file that imports the CSS file, and then
lazy-loading the `.svelte` file works.
-->
<script lang="ts">
import '@fortawesome/fontawesome-free/css/all.css';
</script>
+96 -74
View File
@@ -1,42 +1,53 @@
<script lang="ts">
import Card from '$lib/components/Card/Card.svelte';
import { inputStateStore, getStateString } from '$lib/util/state';
import type { HistoryEntry, HistoryType, State, Tab } from '$lib/types';
import { notify, prompt } from '$lib/util/notify';
import { getStateString, inputStateStore } from '$lib/util/state';
import { logEvent } from '$lib/util/stats';
import dayjs from 'dayjs';
import dayjsRelativeTime from 'dayjs/plugin/relativeTime';
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import BookmarkIcon from '~icons/material-symbols/bookmark-outline-rounded';
import TrashAltIcon from '~icons/material-symbols/delete-outline-rounded';
import DownloadIcon from '~icons/material-symbols/download-rounded';
import SaveIcon from '~icons/material-symbols/save-outline-rounded';
import UndoIcon from '~icons/material-symbols/settings-backup-restore-rounded';
import UploadIcon from '~icons/material-symbols/upload-rounded';
import HistoryIcon from '~icons/mdi/clock-outline';
import GitAltIcon from '~icons/mdi/git';
import { Button } from '../ui/button';
import { Separator } from '../ui/separator';
import {
addHistoryEntry,
historyModeStore,
clearHistoryData,
getPreviousState,
historyModeStore,
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);
const tabSelectHandler = (tab: Tab) => {
historyModeStore.set(tab.id as HistoryType);
};
let tabs: Tab[] = [
let tabs: Tab[] = $state([
{
id: 'manual',
title: 'Saved',
icon: 'far fa-bookmark'
icon: BookmarkIcon
},
{
id: 'auto',
title: 'Timeline',
icon: 'fas fa-history'
icon: HistoryIcon
}
];
]);
const downloadHistory = () => {
const data = get(historyStore);
@@ -92,11 +103,6 @@
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(() => {
@@ -110,77 +116,93 @@
{
id: 'loader',
title: 'Revisions',
icon: 'fab fa-git-alt'
icon: GitAltIcon
},
...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>
<Card onselect={tabSelectHandler} isOpen isClosable={false} {tabs}>
{#snippet actions()}
<div class="flex items-center gap-2">
<Button
size="icon"
variant="ghost"
id="uploadHistory"
onclick={uploadHistory}
title="Upload history"><UploadIcon /></Button>
{#if $historyStore.length > 0}
<Button
id="downloadHistory"
size="icon"
variant="ghost"
onclick={downloadHistory}
title="Download history"><DownloadIcon /></Button>
{/if}
<Separator orientation="vertical" />
<Button
id="saveHistory"
size="icon"
variant="ghost"
onclick={() => saveHistory()}
title="Save current state"><SaveIcon /></Button>
{#if $historyModeStore !== 'loader'}
<Button
id="clearHistory"
size="icon"
variant="ghost"
class="hover:text-destructive"
onclick={() => clearHistory()}
title="Delete all saved states"><TrashAltIcon /></Button>
{/if}
</div>
{/snippet}
<ul class="flex h-full min-w-fit flex-col gap-2 overflow-auto p-2" id="historyList">
{#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>
{#each $historyStore as { id, state, time, name, url, type } (id)}
<li class="flex flex-col gap-2">
<div class="flex items-center justify-between">
<div class="flex flex-col">
{#if url}
<a
href={url}
target="_blank"
title="Open revision in new tab"
class="text-blue-500 hover:underline">{name}</a>
{:else}
<span class="whitespace-nowrap">{name}</span>
{/if}
<span class="whitespace-nowrap text-xs text-primary-foreground/30">
{new Date(time).toLocaleString()}
</span>
</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>
<div class="flex items-center gap-2">
<span class="whitespace-nowrap text-sm text-primary-foreground/50">
{dayjs(time).fromNow()}
</span>
<Button size="icon" variant="ghost" onclick={() => restoreHistoryItem(state)}>
<UndoIcon />
</Button>
{#if type !== 'loader'}
<button class="btn btn-error" on:click={() => clearHistory(id)}
><i class="fas fa-trash-alt mr-1" />Delete</button>
<Button
size="icon"
variant="ghost"
class="hover:text-destructive"
onclick={() => clearHistory(id)}>
<TrashAltIcon />
</Button>
{/if}
</div>
</div>
<Separator />
</li>
{/each}
{:else}
<div class="m-2">
<div class="m-2 text-center">
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.
+5 -5
View File
@@ -1,10 +1,10 @@
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 { localStorage, persist } from '$lib/util/persist';
import { logEvent } from '$lib/util/stats';
import { generateSlug } from 'random-word-slugs';
import type { Readable, Writable } from 'svelte/store';
import { derived, get, writable } from 'svelte/store';
import { v4 as uuidV4 } from 'uuid';
const MAX_AUTO_HISTORY_LENGTH = 30;
+87
View File
@@ -0,0 +1,87 @@
<script lang="ts">
import McWrapper from '$/components/McWrapper.svelte';
import ThemeIcon from '$/components/ThemeIcon.svelte';
import * as Popover from '$/components/ui/popover';
import { Switch } from '$/components/ui/switch';
import { urlsStore } from '$/util/state';
import { cn } from '$/utils';
import { mode, setMode } from 'mode-watcher';
import type { Component } from 'svelte';
import AddIcon from '~icons/material-symbols/add-2-rounded';
import BookIcon from '~icons/material-symbols/book-2-outline-rounded';
import PluginIcon from '~icons/material-symbols/extension-outline';
import HomeIcon from '~icons/material-symbols/house-outline-rounded';
import CommunityIcon from '~icons/material-symbols/person-play-outline-rounded';
import PlaygroundIcon from '~icons/material-symbols/shape-line-outline';
import MermaidChartIcon from './MermaidChartIcon.svelte';
const menuItems = $derived([
{ label: 'New Diagram', icon: AddIcon, href: $urlsStore.new },
{ label: 'Home', icon: HomeIcon, href: 'https://mermaid.js.org/' },
{ label: 'Documentation', icon: BookIcon, href: 'https://mermaid.js.org/intro/' },
{ label: 'Community', icon: CommunityIcon, href: 'https://discord.gg/sKeNQX4Wtj' }
]);
const mermaidChartMenuItems = $derived([
{
label: 'Edit in Playground',
icon: PlaygroundIcon,
href: $urlsStore.mermaidChart({ medium: 'main_menu' }).playground
},
{
label: 'Plugins',
icon: PluginIcon,
href: $urlsStore.mermaidChart({ medium: 'main_menu' }).plugins,
checkDiagramType: false
},
{
label: 'MermaidChart',
icon: MermaidChartIcon,
href: $urlsStore.mermaidChart({ medium: 'main_menu' }).home,
checkDiagramType: false
}
]);
</script>
{#snippet menuItem(options: { label: string; icon: Component; href: string; class?: string })}
<a
href={options.href}
target="_blank"
class={cn(
'flex items-center justify-start gap-2 border-b bg-muted p-2 px-3 hover:bg-background',
options.class
)}>
<options.icon class="size-5" />
{options.label}
</a>
{/snippet}
<Popover.Root>
<Popover.Trigger class="shrink-0">
<img class="size-6" src="/favicon.svg" alt="Mermaid Live Editor" />
</Popover.Trigger>
<Popover.Content align="start" class="flex flex-col overflow-hidden p-0" sideOffset={16}>
{#each menuItems as item}
{@render menuItem(item)}
{/each}
<div class="flex items-center justify-between border-b bg-muted px-3 py-2 hover:bg-background">
<span class="flex items-center gap-2">
<ThemeIcon />
Dark Mode
</span>
<Switch
checked={$mode === 'dark'}
onCheckedChange={(dark) => setMode(dark ? 'dark' : 'light')} />
</div>
{#each mermaidChartMenuItems as item}
<McWrapper side="right" shouldCheckDiagramType={item.checkDiagramType}>
{@render menuItem({
...item,
class: 'text-accent bg-background hover:bg-muted'
})}
</McWrapper>
{/each}
</Popover.Content>
</Popover.Root>
+50
View File
@@ -0,0 +1,50 @@
<script lang="ts">
import { env } from '$/util/env';
import { stateStore } from '$/util/state';
import * as Tooltip from '$lib/components/ui/tooltip';
import type { ComponentProps, Snippet } from 'svelte';
import ExternalLinkIcon from '~icons/material-symbols/open-in-new-rounded';
let {
children,
shouldCheckDiagramType = true,
side = 'bottom',
labelPrefix = 'Opens a new tab in'
}: {
children: Snippet;
shouldCheckDiagramType?: boolean;
side?: ComponentProps<typeof Tooltip.Content>['side'];
labelPrefix?: string;
} = $props();
let shouldDisableComponent = $derived(
shouldCheckDiagramType && $stateStore.diagramType === 'zenuml'
);
</script>
{#if env.isEnabledMermaidChartLinks}
<Tooltip.Provider>
<Tooltip.Root delayDuration={100}>
<Tooltip.Trigger>
<div class={[shouldDisableComponent && 'pointer-events-none cursor-not-allowed grayscale']}>
{@render children()}
</div>
</Tooltip.Trigger>
<Tooltip.Content {side}>
<div class="flex items-center gap-2">
{#if shouldDisableComponent}
<div class="text-muted-foreground">
This diagram type is not supported in MermaidChart.com
</div>
{:else}
<ExternalLinkIcon />
<span class="flex items-center gap-1">
{labelPrefix}
<div class="text-accent">MermaidChart.com</div>
</span>
{/if}
</div>
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
{/if}
@@ -0,0 +1,7 @@
<script lang="ts">
import type { ClassValue } from 'svelte/elements';
let { class: className }: { class?: ClassValue } = $props();
</script>
<img class={['size-4', className]} src="/mermaidchart-logo.svg" alt="Mermaid Chart" />
+99
View File
@@ -0,0 +1,99 @@
<script lang="ts">
import type { EditorProps } from '$/types';
import { stateStore } from '$/util/state';
import { json, jsonLanguage } from '@codemirror/lang-json';
import { markdown } from '@codemirror/lang-markdown';
import { yamlFrontmatter } from '@codemirror/lang-yaml';
import { language } from '@codemirror/language';
import { Compartment, EditorState } from '@codemirror/state';
import { EditorView } from '@codemirror/view';
import { vsCodeDark } from '@fsegurai/codemirror-theme-vscode-dark';
import { vsCodeLight } from '@fsegurai/codemirror-theme-vscode-light';
import { basicSetup } from 'codemirror';
import { mode } from 'mode-watcher';
import { onMount } from 'svelte';
let editorView: EditorView | undefined;
let editorContainer: HTMLDivElement;
let currentText = $state('');
const { onUpdate }: EditorProps = $props();
onMount(() => {
const themeCompartment = new Compartment();
const languageCompartment = new Compartment();
editorView = new EditorView({
state: EditorState.create({
doc: currentText,
extensions: [
basicSetup,
languageCompartment.of([]),
themeCompartment.of([]),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
const newText = update.state.doc.toString();
if (currentText === newText) {
return;
}
currentText = newText;
onUpdate(newText);
}
}),
EditorView.theme({
'&.cm-focused': {
outline: 'none'
},
'&.cm-editor': {
height: '100%'
},
'&.cm-scroller': {
overflow: 'auto'
}
})
]
}),
parent: editorContainer
});
const unsubscribeMode = mode.subscribe((mode) => {
editorView?.dispatch({
effects: themeCompartment.reconfigure(mode === 'dark' ? vsCodeDark : vsCodeLight)
});
});
const unsubscribeState = stateStore.subscribe(({ editorMode, code, mermaid }) => {
const text = editorMode === 'code' ? code : mermaid;
if (currentText === text || !editorView) {
return;
}
currentText = text;
editorView.dispatch({
changes: {
from: 0,
to: editorView.state.doc.length,
insert: text
}
});
const stateLanguage = editorView.state.facet(language);
const isStateJson = stateLanguage === jsonLanguage;
const isCodeJson = editorMode === 'config';
if (stateLanguage && isStateJson === isCodeJson) {
return;
}
editorView.dispatch({
effects: languageCompartment.reconfigure(
isCodeJson ? json() : yamlFrontmatter({ content: markdown() })
)
});
});
return () => {
unsubscribeMode();
unsubscribeState();
editorView?.destroy();
};
});
</script>
<div bind:this={editorContainer} class="size-full"></div>
+95 -131
View File
@@ -1,55 +1,50 @@
<script context="module" lang="ts">
import { version } from 'mermaid/package.json';
<script lang="ts" module>
import { logEvent, plausible } from '$lib/util/stats';
import { version } from 'mermaid/package.json';
void logEvent('version', {
mermaidVersion: version
});
</script>
<script lang="ts">
import Theme from './Theme.svelte';
import MainMenu from '$/components/MainMenu.svelte';
import McWrapper from '$/components/McWrapper.svelte';
import { Button } from '$/components/ui/button';
import { Separator } from '$/components/ui/separator';
import { Switch } from '$/components/ui/switch';
import { dismissPromotion, getActivePromotion } from '$lib/util/promos/promo';
import Privacy from './Privacy.svelte';
let isMenuOpen = false;
import { urlsStore } from '$lib/util/state';
import { MCBaseURL } from '$lib/util/util';
import type { ComponentProps, Snippet } from 'svelte';
import CloseIcon from '~icons/material-symbols/close-rounded';
import GithubIcon from '~icons/mdi/github';
import DropdownNavMenu from './DropdownNavMenu.svelte';
function toggleMenu() {
isMenuOpen = !isMenuOpen;
interface Props {
mobileToggle?: Snippet;
children: Snippet;
}
interface Link {
href: string;
title?: string;
icon?: string;
img?: string;
}
const links: Link[] = [
let { children, mobileToggle }: Props = $props();
const isReferral = document.referrer.includes(MCBaseURL);
type Links = ComponentProps<typeof DropdownNavMenu>['links'];
const githubLinks: Links = [
{ title: 'Mermaid JS', href: 'https://github.com/mermaid-js/mermaid' },
{
title: 'Documentation',
href: 'https://mermaid.js.org/intro/getting-started.html'
title: 'Mermaid Live Editor',
href: 'https://github.com/mermaid-js/mermaid-live-editor'
},
{
title: 'Tutorial',
href: 'https://mermaid.js.org/ecosystem/tutorials.html'
},
{
title: 'Mermaid',
href: 'https://github.com/mermaid-js/mermaid'
},
{
title: 'CLI',
title: 'Mermaid 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();
let activePromotion = $state(getActivePromotion());
const trackBannerClick = () => {
if (!plausible || !activePromotion) {
@@ -62,112 +57,81 @@
</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="top-bar z-10 flex h-fit w-full bg-primary">
<div
class="flex flex-grow"
role="button"
tabindex="0"
on:click={trackBannerClick}
on:keypress={trackBannerClick}>
<svelte:component this={activePromotion.component} />
onclick={trackBannerClick}
onkeypress={trackBannerClick}>
<activePromotion.component {closeBanner} />
</div>
<button
title="Dismiss banner"
on:click={() => {
dismissPromotion(activePromotion?.id);
activePromotion = undefined;
}}>
<i class="fa fa-close px-2" />
</button>
{#snippet closeBanner()}
<Button
title="Dismiss banner"
variant="ghost"
class="hover:bg-transparent hover:text-[#261A56]"
size="sm"
onclick={() => {
dismissPromotion(activePromotion?.id);
activePromotion = undefined;
}}>
<CloseIcon />
</Button>
{/snippet}
</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>
<nav class="z-50 flex p-4 sm:p-6">
<div class="flex flex-1 items-center gap-4">
<MainMenu />
<div
id="switcher"
class="flex items-center justify-center gap-4 font-medium"
class:flex-row-reverse={isReferral}>
<a href="/" class="whitespace-nowrap text-accent">
{#if !isReferral && !mobileToggle}
Mermaid
{/if}
Live Editor
</a>
<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>
<McWrapper labelPrefix="Opens the current diagram in">
<div class="hidden items-center justify-center gap-4 md:flex">
<Separator orientation="vertical" />
<Switch
id="editorMode"
class="data-[state=checked]:bg-secondary"
checked={isReferral}
onclick={() => {
logEvent('playgroundToggle', { isReferred: isReferral });
// Wait for the event to be logged
setTimeout(() => {
window.open(
$urlsStore.mermaidChart({ medium: 'toggle' }).playground,
'_self',
// Do not send referrer header, if the user already came from playground
isReferral ? 'noreferrer' : ''
);
}, 100);
}} />
<!-- 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
href={$urlsStore.mermaidChart({ medium: 'toggle' }).playground}
class="whitespace-nowrap">
Playground <span class="hidden text-sm opacity-50 lg:inline"
>- more features, no account required</span>
</a>
</li>
{/each}
</ul>
</div>
</McWrapper>
</div>
</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>
<div
id="menu"
class="hidden flex-nowrap items-center justify-between gap-3 overflow-hidden md:flex">
<DropdownNavMenu icon={GithubIcon} links={githubLinks} />
<Separator orientation="vertical" />
{@render children()}
</div>
{@render mobileToggle?.()}
</nav>
+34
View File
@@ -0,0 +1,34 @@
<script lang="ts">
import FloatingToolbar from '$/components/FloatingToolbar.svelte';
import { Button } from '$/components/ui/button';
import { Separator } from '$/components/ui/separator';
import type { PanZoomState } from '$/util/panZoom';
import { urlsStore } from '$/util/state';
import ExpandIcon from '~icons/material-symbols/open-in-full-rounded';
import ArrowsToCircleIcon from '~icons/material-symbols/screenshot-frame-2';
import MagnifyingGlassPlusIcon from '~icons/material-symbols/zoom-in';
import MagnifyingGlassMinusIcon from '~icons/material-symbols/zoom-out';
let { panZoomState }: { panZoomState: PanZoomState } = $props();
</script>
<FloatingToolbar>
<Button variant="ghost" size="icon" title="Reset view" onclick={() => panZoomState.reset()}>
<ArrowsToCircleIcon />
</Button>
<Separator orientation="vertical" />
<Button
variant="ghost"
size="icon"
class="hidden sm:block"
onclick={() => panZoomState.zoomOut()}>
<MagnifyingGlassMinusIcon />
</Button>
<Button variant="ghost" size="icon" class="hidden sm:block" onclick={() => panZoomState.zoomIn()}>
<MagnifyingGlassPlusIcon />
</Button>
<Separator orientation="vertical" class="hidden sm:block" />
<Button variant="ghost" size="icon" title="Full Screen" href={$urlsStore.view} target="_blank">
<ExpandIcon />
</Button>
</FloatingToolbar>
+96 -75
View File
@@ -1,20 +1,30 @@
<script lang="ts">
import Card from '$/components/Card/Card.svelte';
import { Button } from '$/components/ui/button';
import { updateCode } from '$lib/util/state';
import Card from '$lib/components/Card/Card.svelte';
import { logEvent } from '$lib/util/stats';
import ShapesIcon from '~icons/material-symbols/account-tree-outline-rounded';
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!`,
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
`,
Class: `classDiagram
Animal <|-- Duck
Animal <|-- Fish
@@ -36,26 +46,6 @@
+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
@@ -65,15 +55,21 @@
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`,
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]`,
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`,
Git: `gitGraph
commit
commit
@@ -93,7 +89,7 @@
Popularisation
British popular psychology author Tony Buzan
Research
On effectivness<br/>and features
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
@@ -102,6 +98,32 @@
Tools
Pen and paper
Mermaid`,
Packet: `---
title: "TCP Packet"
---
packet-beta
0-15: "Source Port"
16-31: "Destination Port"
32-63: "Sequence Number"
64-95: "Acknowledgment Number"
96-99: "Data Offset"
100-105: "Reserved"
106: "URG"
107: "ACK"
108: "PSH"
109: "RST"
110: "SYN"
111: "FIN"
112-127: "Window"
128-143: "Checksum"
144-159: "Urgent Pointer"
160-191: "(Options and Padding)"
192-255: "Data (variable length)"
`,
Pie: `pie title Pets adopted by volunteers
"Dogs" : 386
"Cats" : 85
"Rats" : 15`,
QuadrantChart: `quadrantChart
title Reach and engagement of campaigns
x-axis Low Reach --> High Reach
@@ -116,6 +138,27 @@
Campaign D: [0.78, 0.34]
Campaign E: [0.40, 0.34]
Campaign F: [0.35, 0.78]`,
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!`,
State: `stateDiagram-v2
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`,
'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`,
XYChart: `
xychart-beta
title "Sales Revenue"
@@ -123,25 +166,6 @@
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
@@ -171,14 +195,12 @@
type SampleTypes = keyof typeof samples;
const loadSampleDiagram = (diagramType: SampleTypes): void => {
updateCode(samples[diagramType], {
updateDiagram: true,
resetPanZoom: true
resetPanZoom: true,
updateDiagram: 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',
@@ -190,24 +212,23 @@
'Git',
'Pie',
'Mindmap',
'ZenUML',
'QuadrantChart',
'XYChart',
'Block',
'ZenUML'
'Packet'
];
</script>
<Card title="Sample Diagrams" isOpen={false}>
<div class="flex flex-wrap gap-2 p-2">
<Card title="Sample Diagrams" isOpen isStackable icon={{ component: ShapesIcon }}>
<div class="flex h-fit max-h-52 flex-wrap gap-2 overflow-y-auto 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)}>
<Button
size="sm"
class="w-fit min-w-20 flex-grow normal-case"
onclick={() => loadSampleDiagram(sample)}>
{sample}
{#if newDiagrams.includes(sample)}
<span class="fa fa-heart ml-2" />
{/if}
</button>
</Button>
{/each}
</div>
</Card>
+37 -26
View File
@@ -1,31 +1,46 @@
<label for="privacyModal" class="btn btn-ghost flex gap-2">
<i class="fa-solid fa-shield-halved text-xl"></i> Security
</label>
<script>
import * as Dialog from '$/components/ui/dialog';
import ShieldIcon from '~icons/material-symbols/shield-lock-outline-rounded';
</script>
<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>
<Dialog.Root>
<Dialog.Trigger>
<ShieldIcon />
</Dialog.Trigger>
<Dialog.Content class="max-h-full overflow-y-auto">
<Dialog.Header>
<Dialog.Title class="flex items-center gap-2 text-xl">
<ShieldIcon class="size-8 text-green-700" />
Data security
</Dialog.Title>
</Dialog.Header>
<p class="text-xl font-semibold">
The content of the diagrams you create never leaves your browser.
</p>
<p>It's only stored in the URL, and your browser's local storage.</p>
<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.
Mermaid Live Editor is a fully open source, client side application, deployed transparently on <a
href="https://github.com/mermaid-js/mermaid-live-editor/deployments"
class="underline"
target="_blank">
GitHub Pages
</a>.
</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.
It will also work as a fully offline
<a href="https://web.dev/explore/progressive-web-apps" target="_blank">
Progressive Web App.
</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">
Plausible Analytics. We only collect anonymous data related to actions performed, like the
type of diagram rendered, number of times a feature was used, etc.
</p>
<p>
All the data we collect is anonymized and
<a href="https://p.mermaid.live/mermaid.live" class="underline" target="_blank">
available publicly.
</a>
</p>
@@ -34,9 +49,5 @@
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>
</Dialog.Content>
</Dialog.Root>
+48
View File
@@ -0,0 +1,48 @@
<script>
import { buttonVariants } from '$/components/ui/button';
import * as Dialog from '$/components/ui/dialog';
import { Separator } from '$/components/ui/separator';
import { env } from '$/util/env';
import { urlsStore } from '$/util/state';
import ShareIcon from '~icons/material-symbols/share';
import CopyInput from './CopyInput.svelte';
import MermaidChartIcon from './MermaidChartIcon.svelte';
</script>
<Dialog.Root>
<Dialog.Trigger class={buttonVariants({ size: 'sm' })}>Share</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title class="flex items-center gap-2 text-xl">
<ShareIcon class="size-5" /> Shareable links
</Dialog.Title>
<Dialog.Description>Share your diagrams with others.</Dialog.Description>
</Dialog.Header>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<h2 class="flex items-center gap-2">
<img class="size-5" src="/favicon.svg" alt="Mermaid Live Editor" />
Mermaid Live Editor
</h2>
<CopyInput value={window.location.href} />
<Dialog.Description>
The content of the diagrams you create never leaves your browser.
</Dialog.Description>
</div>
{#if env.isEnabledMermaidChartLinks}
<Separator />
<div class="flex flex-col gap-2">
<h2 class="flex items-center gap-2">
<MermaidChartIcon class="size-5" />
Mermaid Chart Playground
</h2>
<CopyInput value={$urlsStore.mermaidChart({ medium: 'share' }).playground} />
<Dialog.Description>
Opens the Mermaid Chart Playground with Mermaid AI, Visual Editor, and more.
</Dialog.Description>
</div>
{/if}
</div>
</Dialog.Content>
</Dialog.Root>
@@ -0,0 +1,21 @@
<script lang="ts">
import FloatingToolbar from '$/components/FloatingToolbar.svelte';
import { Toggle } from '$/components/ui/toggle';
import { defaultState, inputStateStore } from '$/util/state';
import RoughIcon from '~icons/material-symbols/draw-outline-rounded';
import BackgroundIcon from '~icons/material-symbols/grid-4x4-rounded';
if ($inputStateStore.grid === undefined) {
// Handle cases where old states were saved without grid option
$inputStateStore.grid = defaultState.grid;
}
</script>
<FloatingToolbar>
<Toggle bind:pressed={$inputStateStore.rough} size="sm" title="Hand-Drawn">
<RoughIcon />
</Toggle>
<Toggle bind:pressed={$inputStateStore.grid} size="sm" title="Background Grid">
<BackgroundIcon />
</Toggle>
</FloatingToolbar>
-66
View File
@@ -1,66 +0,0 @@
<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>
+39
View File
@@ -0,0 +1,39 @@
<script lang="ts">
import { mode } from 'mode-watcher';
import { cubicInOut } from 'svelte/easing';
import { type TransitionConfig } from 'svelte/transition';
import MoonIcon from '~icons/material-symbols/dark-mode-outline-rounded';
import SunIcon from '~icons/material-symbols/light-mode-outline-rounded';
const spin = (
node: Element,
{ duration = 400, easing = cubicInOut, clockWise = true } = {}
): TransitionConfig => {
const style = getComputedStyle(node);
const opacity = +style.opacity;
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration,
easing,
css: (t, u) => `
transform: ${transform} rotate(${u * 90 * (clockWise ? 1 : -1)}deg);
opacity: ${opacity * t}
`
};
};
</script>
<div class="inline-grid">
{#key $mode}
<div
in:spin={{ clockWise: true }}
out:spin={{ clockWise: false }}
class="col-start-1 row-start-1">
{#if $mode === 'dark'}
<MoonIcon />
{:else}
<SunIcon />
{/if}
</div>
{/key}
</div>
@@ -0,0 +1,28 @@
<script lang="ts">
import FloatingToolbar from '$/components/FloatingToolbar.svelte';
import Privacy from '$/components/Privacy.svelte';
import { Button } from '$/components/ui/button';
import { Separator } from '$/components/ui/separator';
import { TID } from '$/constants';
import { version } from 'mermaid/package.json';
import { mode, setMode } from 'mode-watcher';
import ThemeIcon from './ThemeIcon.svelte';
</script>
<FloatingToolbar>
<span class="text-sm font-semibold opacity-60">v{version}</span>
<Button variant="ghost" size="icon" title="Privacy & Security">
<Privacy />
</Button>
<Separator orientation="vertical" />
<Button
variant="ghost"
size="icon"
data-testid={TID.themeToggleButton}
title="Switch to {$mode === 'dark' ? 'light' : 'dark'} theme"
class="[&_svg]:size-5"
onclick={() => setMode($mode === 'dark' ? 'light' : 'dark')}>
<ThemeIcon />
</Button>
</FloatingToolbar>
+69 -114
View File
@@ -1,112 +1,89 @@
<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 { State, ValidatedState } from '$/types';
import { recordRenderTime, shouldRefreshView } from '$/util/autoSync';
import { render as renderDiagram } from '$/util/mermaid';
import { PanZoomState } from '$/util/panZoom';
import { inputStateStore, stateStore, updateCodeStore } from '$/util/state';
import { logEvent, saveStatistics } from '$/util/stats';
import FontAwesome, { mayContainFontAwesome } from '$lib/components/FontAwesome.svelte';
import uniqueID from 'lodash-es/uniqueId';
import type { MermaidConfig } from 'mermaid';
import { mode } from 'mode-watcher';
import { onMount } from 'svelte';
import panzoom from 'svg-pan-zoom';
import { Svg2Roughjs } from 'svg2roughjs';
let {
panZoomState = new PanZoomState(),
shouldShowGrid = true
}: { panZoomState?: PanZoomState; shouldShowGrid?: boolean } = $props();
let code = '';
let config = '';
let container: HTMLDivElement;
let container: HTMLDivElement | undefined = $state();
let rough: boolean;
let view: HTMLDivElement;
let error = false;
let errorLines: string[] = [];
let outOfSync = false;
let hide = false;
let view: HTMLDivElement | undefined = $state();
let error = $state(false);
let panZoom = true;
let manualUpdate = true;
let panZoomEnabled = $stateStore.panZoom;
let pzoom: typeof panzoom | undefined;
let waitForFontAwesomeToLoad: FontAwesome['waitForFontAwesomeToLoad'] | undefined = $state();
const handlePanZoomChange = () => {
if (!pzoom) {
return;
}
const pan = pzoom.getPan();
const zoom = pzoom.getZoom();
updateCodeStore({ pan, zoom });
logEvent('panZoom');
// Set up panZoom state observer to update the store when pan/zoom changes
const setupPanZoomObserver = () => {
panZoomState.onPanZoomChange = (pan, zoom) => {
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 handlePanZoom = (state: State, graphDiv: SVGSVGElement) => {
panZoomState.updateElement(graphDiv, state);
};
const handleStateChange = async (state: ValidatedState) => {
const startTime = Date.now();
if (state.error !== undefined) {
error = true;
errorLines = state.error.toString().split('\n');
return;
}
error = false;
let diagramType: string | undefined;
try {
if (container && state && (state.updateDiagram || state.autoSync)) {
if (!state.autoSync) {
$inputStateStore.updateDiagram = false;
}
outOfSync = false;
if (container) {
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
rough === state.rough &&
panZoom === state.panZoom
) {
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'
);
panZoom = state.panZoom ?? true;
if (mayContainFontAwesome(code)) {
await waitForFontAwesomeToLoad?.();
}
const scroll = view?.parentElement?.scrollTop;
delete container.dataset.processed;
const viewID = uniqueID('graph-');
const {
svg,
bindFunctions,
diagramType: detectedDiagramType
} = await renderDiagram(JSON.parse(state.mermaid) as MermaidConfig, code, viewID);
diagramType = detectedDiagramType;
if (svg.length > 0) {
handlePanZoom(state);
container.innerHTML = svg;
const graphDiv = document.querySelector<SVGSVGElement>('#graph-div');
let graphDiv = document.querySelector<SVGSVGElement>(`#${viewID}`);
if (!graphDiv) {
throw new Error('graph-div not found');
}
@@ -115,16 +92,18 @@
svg2roughjs.svg = graphDiv;
await svg2roughjs.sketch();
graphDiv.remove();
const sketch = document.querySelector<HTMLElement>('#container > svg');
const sketch = document.querySelector<SVGSVGElement>('#container > svg');
if (!sketch) {
throw new Error('sketch not found');
}
const height = sketch.getAttribute('height');
const width = sketch.getAttribute('width');
sketch.setAttribute('id', 'graph-div');
sketch.setAttribute('height', '100%');
sketch.setAttribute('width', '100%');
sketch.setAttribute('viewBox', `0 0 ${width} ${height}`);
sketch.style.maxWidth = '100%';
graphDiv = sketch;
} else {
graphDiv.setAttribute('height', '100%');
graphDiv.style.maxWidth = '100%';
@@ -132,79 +111,55 @@
bindFunctions(graphDiv);
}
}
if (state.panZoom) {
handlePanZoom(state, graphDiv);
}
}
if (view.parentElement && scroll) {
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 });
saveStatistics({ code, diagramType, isRough: state.rough, renderTime });
recordRenderTime(renderTime, () => {
$inputStateStore.updateDiagram = true;
});
};
onMount(() => {
setupPanZoomObserver();
// Queue state changes to avoid race condition
let pendingStateChange = Promise.resolve();
stateStore.subscribe((state) => {
void handleStateChange(state);
});
window.addEventListener('resize', () => {
if ($stateStore.panZoom && pzoom) {
pzoom.resize();
}
pendingStateChange = pendingStateChange.then(() => handleStateChange(state).catch(() => {}));
});
});
</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}
<FontAwesome bind:waitForFontAwesomeToLoad />
<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
id="view"
bind:this={view}
class={['h-full w-full', shouldShowGrid && `grid-bg-${$mode}`, error && 'opacity-50']}>
<div id="container" bind:this={container} class="h-full overflow-auto"></div>
</div>
<style>
#view {
flex: 1;
.grid-bg-light {
background-size: 30px 30px;
background-image: radial-gradient(circle, #e4e4e48c 2px, #0000 2px);
}
#container {
transition: visibility 0.3s;
}
.error,
.outOfSync {
opacity: 0.5;
}
.hide {
visibility: hidden;
.grid-bg-dark {
background-size: 30px 30px;
background-image: radial-gradient(circle, #46464646 2px, #0000 2px);
}
</style>
@@ -0,0 +1,69 @@
<script lang="ts" module>
import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
export const buttonVariants = tv({
base: 'focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-6 [&_svg]:shrink-0',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/80',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm',
outline:
'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
accent: 'bg-accent text-accent-foreground hover:bg-accent/80 shadow-sm',
ghost: 'hover:bg-primary hover:text-primary-foreground',
link: 'text-primary underline-offset-4 hover:underline'
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
icon: 'size-8'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
});
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
};
</script>
<script lang="ts">
import { cn } from '$lib/utils.js';
let {
class: className,
variant = 'default',
size = 'default',
ref = $bindable(null),
href,
type = 'button',
children,
...restProps
}: ButtonProps = $props();
</script>
{#if href}
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
{@render children?.()}
</a>
{:else}
<button
bind:this={ref}
class={cn(buttonVariants({ variant, size }), className)}
{type}
{...restProps}>
{@render children?.()}
</button>
{/if}
+9
View File
@@ -0,0 +1,9 @@
export {
default as Button,
buttonVariants,
default as Root,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
type ButtonProps as Props
} from './button.svelte';
@@ -0,0 +1,36 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import X from 'lucide-svelte/icons/x';
import type { Snippet } from 'svelte';
import * as Dialog from './index.js';
let {
ref = $bindable(null),
class: className,
portalProps,
children,
...restProps
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
portalProps?: DialogPrimitive.PortalProps;
children: Snippet;
} = $props();
</script>
<Dialog.Portal {...portalProps}>
<Dialog.Overlay />
<DialogPrimitive.Content
bind:ref
class={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className
)}
{...restProps}>
{@render children?.()}
<DialogPrimitive.Close
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:text-muted-foreground">
<X class="size-4" />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</Dialog.Portal>
@@ -0,0 +1,15 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.DescriptionProps = $props();
</script>
<DialogPrimitive.Description
bind:ref
class={cn('text-sm text-muted-foreground', className)}
{...restProps} />
@@ -0,0 +1,19 @@
<script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
{...restProps}>
{@render children?.()}
</div>
@@ -0,0 +1,19 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)}
{...restProps}>
{@render children?.()}
</div>
@@ -0,0 +1,18 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.OverlayProps = $props();
</script>
<DialogPrimitive.Overlay
bind:ref
class={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className
)}
{...restProps} />
@@ -0,0 +1,15 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.TitleProps = $props();
</script>
<DialogPrimitive.Title
bind:ref
class={cn('text-lg font-semibold leading-none tracking-tight', className)}
{...restProps} />
+36
View File
@@ -0,0 +1,36 @@
import { Dialog as DialogPrimitive } from 'bits-ui';
import Content from './dialog-content.svelte';
import Description from './dialog-description.svelte';
import Footer from './dialog-footer.svelte';
import Header from './dialog-header.svelte';
import Overlay from './dialog-overlay.svelte';
import Title from './dialog-title.svelte';
const Root: typeof DialogPrimitive.Root = DialogPrimitive.Root;
const Trigger: typeof DialogPrimitive.Trigger = DialogPrimitive.Trigger;
const Close: typeof DialogPrimitive.Close = DialogPrimitive.Close;
const Portal: typeof DialogPrimitive.Portal = DialogPrimitive.Portal;
export {
Close,
Content,
Description,
//
Root as Dialog,
Close as DialogClose,
Content as DialogContent,
Description as DialogDescription,
Footer as DialogFooter,
Header as DialogHeader,
Overlay as DialogOverlay,
Portal as DialogPortal,
Title as DialogTitle,
Trigger as DialogTrigger,
Footer,
Header,
Overlay,
Portal,
Root,
Title,
Trigger
};
+7
View File
@@ -0,0 +1,7 @@
import Root from './input.svelte';
export {
Root,
//
Root as Input
};
+43
View File
@@ -0,0 +1,43 @@
<script lang="ts">
import type { InputType } from '$/types';
import { cn } from '$lib/utils.js';
import type { WithElementRef } from 'bits-ui';
import type { HTMLInputAttributes } from 'svelte/elements';
type Props = WithElementRef<
Omit<HTMLInputAttributes, 'type'> &
({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })
>;
let {
ref = $bindable(null),
value = $bindable(),
type,
files = $bindable(),
class: className,
...restProps
}: Props = $props();
</script>
{#if type === 'file'}
<input
bind:this={ref}
class={cn(
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
type="file"
bind:files
bind:value
{...restProps} />
{:else}
<input
bind:this={ref}
class={cn(
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className
)}
{type}
bind:value
{...restProps} />
{/if}
+17
View File
@@ -0,0 +1,17 @@
import { Popover as PopoverPrimitive } from 'bits-ui';
import Content from './popover-content.svelte';
const Root = PopoverPrimitive.Root;
const Trigger = PopoverPrimitive.Trigger;
const Close = PopoverPrimitive.Close;
export {
Root,
Content,
Trigger,
Close,
//
Root as Popover,
Content as PopoverContent,
Trigger as PopoverTrigger,
Close as PopoverClose
};
@@ -0,0 +1,27 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Popover as PopoverPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
align = 'center',
sideOffset = 4,
portalProps,
...restProps
}: PopoverPrimitive.ContentProps & {
portalProps?: PopoverPrimitive.PortalProps;
} = $props();
</script>
<PopoverPrimitive.Portal {...portalProps}>
<PopoverPrimitive.Content
bind:ref
{align}
{sideOffset}
class={cn(
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...restProps} />
</PopoverPrimitive.Portal>
+13
View File
@@ -0,0 +1,13 @@
import { Pane } from 'paneforge';
import Handle from './resizable-handle.svelte';
import PaneGroup from './resizable-pane-group.svelte';
export {
PaneGroup,
Pane,
Handle,
//
PaneGroup as ResizablePaneGroup,
Pane as ResizablePane,
Handle as ResizableHandle
};
@@ -0,0 +1,29 @@
<script lang="ts">
import GripVertical from 'lucide-svelte/icons/grip-vertical';
import * as ResizablePrimitive from 'paneforge';
import type { WithoutChildrenOrChild } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
withHandle = false,
...restProps
}: WithoutChildrenOrChild<ResizablePrimitive.PaneResizerProps> & {
withHandle?: boolean;
} = $props();
</script>
<ResizablePrimitive.PaneResizer
bind:ref
class={cn(
'relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[direction=vertical]:h-px data-[direction=vertical]:w-full data-[direction=vertical]:after:left-0 data-[direction=vertical]:after:h-1 data-[direction=vertical]:after:w-full data-[direction=vertical]:after:-translate-y-1/2 data-[direction=vertical]:after:translate-x-0 [&[data-direction=vertical]>div]:rotate-90',
className
)}
{...restProps}>
{#if withHandle}
<div class="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<GripVertical class="size-2.5" />
</div>
{/if}
</ResizablePrimitive.PaneResizer>
@@ -0,0 +1,21 @@
<script lang="ts">
import * as ResizablePrimitive from 'paneforge';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
direction,
this: paneGroup = $bindable(),
...restProps
}: ResizablePrimitive.PaneGroupProps & {
this?: ResizablePrimitive.PaneGroup;
} = $props();
</script>
<ResizablePrimitive.PaneGroup
bind:ref
bind:this={paneGroup}
{direction}
class={cn('flex h-full w-full data-[direction=vertical]:flex-col', className)}
{...restProps} />
+7
View File
@@ -0,0 +1,7 @@
import Root from './separator.svelte';
export {
Root,
//
Root as Separator
};
@@ -0,0 +1,21 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Separator as SeparatorPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
orientation = 'horizontal',
...restProps
}: SeparatorPrimitive.RootProps = $props();
</script>
<SeparatorPrimitive.Root
bind:ref
class={cn(
'shrink-0 rounded border',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'min-h-6 w-[0px]',
className
)}
{orientation}
{...restProps} />
+1
View File
@@ -0,0 +1 @@
export { default as Toaster } from './sonner.svelte';
@@ -0,0 +1,20 @@
<script lang="ts">
import { Toaster as Sonner, type ToasterProps as SonnerProps } from 'svelte-sonner';
import { mode } from 'mode-watcher';
let restProps: SonnerProps = $props();
</script>
<Sonner
theme={$mode}
class="toaster group"
toastOptions={{
classes: {
toast:
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
description: 'group-[.toast]:text-muted-foreground',
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground'
}
}}
{...restProps} />
+7
View File
@@ -0,0 +1,7 @@
import Root from './switch.svelte';
export {
Root,
//
Root as Switch
};
@@ -0,0 +1,25 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Switch as SwitchPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
let {
ref = $bindable(null),
checked = $bindable(false),
class: className,
...restProps
}: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
</script>
<SwitchPrimitive.Root
bind:ref
bind:checked
class={cn(
'peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-accent data-[state=unchecked]:bg-slate-700',
className
)}
{...restProps}>
<SwitchPrimitive.Thumb
class={cn(
'pointer-events-none block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0'
)} />
</SwitchPrimitive.Root>
@@ -0,0 +1,10 @@
import Root from './toggle-group.svelte';
import Item from './toggle-group-item.svelte';
export {
Root,
Item,
//
Root as ToggleGroup,
Item as ToggleGroupItem
};
@@ -0,0 +1,29 @@
<script lang="ts">
import { ToggleGroup as ToggleGroupPrimitive } from 'bits-ui';
import { getToggleGroupCtx } from './toggle-group.svelte';
import { cn } from '$lib/utils.js';
import { type ToggleVariants, toggleVariants } from '$lib/components/ui/toggle/index.js';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
size,
variant,
...restProps
}: ToggleGroupPrimitive.ItemProps & ToggleVariants = $props();
const ctx = getToggleGroupCtx();
</script>
<ToggleGroupPrimitive.Item
bind:ref
class={cn(
toggleVariants({
variant: ctx.variant || variant,
size: ctx.size || size
}),
className
)}
{value}
{...restProps} />
@@ -0,0 +1,40 @@
<script lang="ts" module>
import { getContext, setContext } from 'svelte';
import type { ToggleVariants } from '$lib/components/ui/toggle/index.js';
export function setToggleGroupCtx(props: ToggleVariants) {
setContext('toggleGroup', props);
}
export function getToggleGroupCtx() {
return getContext<ToggleVariants>('toggleGroup');
}
</script>
<script lang="ts">
import { ToggleGroup as ToggleGroupPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
size = 'default',
variant = 'default',
...restProps
}: ToggleGroupPrimitive.RootProps & ToggleVariants = $props();
setToggleGroupCtx({
variant,
size
});
</script>
<!--
Discriminated Unions + Destructing (required for bindable) do not
get along, so we shut typescript up by casting `value` to `never`.
-->
<ToggleGroupPrimitive.Root
bind:value={value as never}
bind:ref
class={cn('flex items-center justify-center gap-1', className)}
{...restProps} />
+13
View File
@@ -0,0 +1,13 @@
import Root from './toggle.svelte';
export {
toggleVariants,
type ToggleSize,
type ToggleVariant,
type ToggleVariants
} from './toggle.svelte';
export {
Root,
//
Root as Toggle
};
@@ -0,0 +1,50 @@
<script lang="ts" module>
import { type VariantProps, tv } from 'tailwind-variants';
export const toggleVariants = tv({
base: 'hover:bg-muted hover:text-muted-foreground focus-visible:ring-ring data-[state=on]:bg-primary data-[state=on]:text-primary-foreground inline-flex items-center justify-center gap-1 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-6 [&_svg]:shrink-0',
variants: {
variant: {
default: 'bg-transparent',
outline:
'border-input hover:bg-primary/80 hover:text-primary-foreground border bg-transparent'
},
size: {
default: 'h-9 min-w-9 px-3',
sm: 'h-8 min-w-8 px-2',
lg: 'h-10 min-w-10 px-3'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
});
export type ToggleVariant = VariantProps<typeof toggleVariants>['variant'];
export type ToggleSize = VariantProps<typeof toggleVariants>['size'];
export type ToggleVariants = VariantProps<typeof toggleVariants>;
</script>
<script lang="ts">
import { cn } from '$lib/utils.js';
import { Toggle as TogglePrimitive } from 'bits-ui';
let {
ref = $bindable(null),
pressed = $bindable(false),
class: className,
size = 'default',
variant = 'default',
...restProps
}: TogglePrimitive.RootProps & {
variant?: ToggleVariant;
size?: ToggleSize;
} = $props();
</script>
<TogglePrimitive.Root
bind:ref
bind:pressed
class={cn(toggleVariants({ variant, size }), className)}
{...restProps} />
+18
View File
@@ -0,0 +1,18 @@
import { Tooltip as TooltipPrimitive } from 'bits-ui';
import Content from './tooltip-content.svelte';
const Root = TooltipPrimitive.Root;
const Trigger = TooltipPrimitive.Trigger;
const Provider = TooltipPrimitive.Provider;
export {
Content,
Provider,
Root,
//
Root as Tooltip,
Content as TooltipContent,
Provider as TooltipProvider,
Trigger as TooltipTrigger,
Trigger
};
@@ -0,0 +1,20 @@
<script lang="ts">
import { Tooltip as TooltipPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
...restProps
}: TooltipPrimitive.ContentProps = $props();
</script>
<TooltipPrimitive.Content
bind:ref
{sideOffset}
class={cn(
'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...restProps} />
+12
View File
@@ -0,0 +1,12 @@
export const TID = {
copyMarkdown: 'copy-markdown',
diagramDocumentationButton: 'diagram-documentation-button',
downloadPNG: 'download-PNG',
downloadSVG: 'download-SVG',
errorContainer: 'error-container',
themeToggleButton: 'theme-toggle-button'
} as const;
export const C = {
utmSource: 'mermaid_live_editor'
} as const;
+25 -7
View File
@@ -1,3 +1,7 @@
import type { Component } from 'svelte';
import type { HTMLInputTypeAttribute } from 'svelte/elements';
import 'unplugin-icons/types/svelte';
export interface MarkerData {
severity: number;
message: string;
@@ -15,17 +19,19 @@ export interface TabEvents {
export interface Tab {
id: string;
title: string;
icon: string;
icon: Component;
}
export interface State {
code: string;
mermaid: string;
updateDiagram: boolean;
autoSync: boolean;
rough: boolean;
editorMode?: EditorMode;
// All new options must be optional, as users would have old states saved
renderCount?: number;
panZoom?: boolean;
grid?: boolean;
editorMode?: EditorMode;
pan?: { x: number; y: number };
zoom?: number;
loader?: LoaderConfig;
@@ -33,6 +39,7 @@ export interface State {
export interface ValidatedState extends State {
editorMode: EditorMode;
diagramType?: string;
error?: Error;
errorMarkers: MarkerData[];
serialized: string;
@@ -50,10 +57,15 @@ export interface FileLoaderConfig {
codeURL: string;
configURL?: string;
}
export interface LoaderConfig {
type: 'gist' | 'files';
config: GistLoaderConfig | FileLoaderConfig;
}
export type LoaderConfig =
| {
type: 'gist';
config: GistLoaderConfig;
}
| {
type: 'files';
config: FileLoaderConfig;
};
export type HistoryType = 'auto' | 'manual' | 'loader';
export type HistoryEntry = { id: string; state: State; time: number; url?: string } & (
| {
@@ -87,3 +99,9 @@ export interface ErrorHash {
last_column: number;
};
}
export type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
export interface EditorProps {
onUpdate: (text: string) => void;
}
-6
View File
@@ -1,6 +1,4 @@
import debounce from 'lodash-es/debounce';
import { get } from 'svelte/store';
import { stateStore } from './state';
let shouldSync = true;
let updater: () => void;
@@ -16,10 +14,6 @@ const debouncedRender = debounce(() => {
export const recordRenderTime = (renderTimeMs: number, updaterFunction: () => void): void => {
resolveRenderPromise?.();
const { autoSync } = get(stateStore);
if (!autoSync) {
return;
}
updater = updaterFunction;
const isSlow = renderTimeMs > slowRenderThreshold;
if (!shouldSync) {

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