deploy: 3ca2075f654eb003bc2badb86f8456b4630620ca
@@ -1,38 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'prettier'
|
||||
],
|
||||
plugins: ['svelte3', 'tailwindcss', '@typescript-eslint'],
|
||||
ignorePatterns: ['docs/*', '*.cjs', 'snapshots.js', 'svelte.config.js', 'package.json'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
settings: {
|
||||
'svelte3/typescript': () => require('typescript')
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2019,
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-ignore': 'allow-with-description'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'warn'
|
||||
}
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Link to Live Editor:
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,7 +0,0 @@
|
||||
name: 'Mermaid CodeQL config'
|
||||
|
||||
paths:
|
||||
- src
|
||||
paths-ignore:
|
||||
- node_modules
|
||||
- '**/*.spec.js'
|
||||
@@ -1,8 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm'
|
||||
directory: '/'
|
||||
target-branch: 'develop'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
day: 'friday'
|
||||
@@ -1,17 +0,0 @@
|
||||
## :bookmark_tabs: Summary
|
||||
|
||||
Brief description about the content of your PR:
|
||||
|
||||
Resolves #<your issue id here>
|
||||
|
||||
## :straight_ruler: Design Decisions
|
||||
|
||||
Describe the way your implementation works or what design decisions you made if applicable:
|
||||
|
||||
### :clipboard: Tasks
|
||||
|
||||
Make sure you
|
||||
|
||||
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
|
||||
- [ ] :computer: have added unit/e2e tests (if appropriate)
|
||||
- [ ] :bookmark: targeted `master` branch
|
||||
@@ -1,68 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '36 1 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['javascript']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
config-file: ./.github/codeql/config.yml
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
# - name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Cypress Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
cypress-run:
|
||||
runs-on: ubuntu-latest
|
||||
container: cypress/browsers:node14.16.0-chrome90-ff88
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-and-build-cache
|
||||
with:
|
||||
path: |
|
||||
~/.cache/Cypress
|
||||
build
|
||||
node_modules
|
||||
key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-build-
|
||||
|
||||
# Install NPM dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
build: yarn build
|
||||
start: yarn preview
|
||||
wait-on: 'http://localhost:3000'
|
||||
wait-on-timeout: 120
|
||||
# record: true
|
||||
headless: true
|
||||
env:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
@@ -1,57 +0,0 @@
|
||||
name: Deploy Live Editor
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: yarn-and-build-cache
|
||||
with:
|
||||
path: |
|
||||
node_modules
|
||||
key: ${{ runner.os }}-node_modules-build-deploy-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node_modules-build-deploy-
|
||||
|
||||
- name: Setup Beta
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
export BETA=true
|
||||
rm -rf docs/beta/
|
||||
|
||||
- name: Cleanup build
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
run: |
|
||||
rm -rf docs/_app/
|
||||
|
||||
- name: Build & Deploy
|
||||
run: |
|
||||
npm i -g yarn
|
||||
export DEPLOY=true
|
||||
echo "$GITHUB_EVENT_NAME"
|
||||
yarn install
|
||||
# yarn run lint
|
||||
version=$(yarn version --patch --no-git-tag-version | grep "New version" | cut -d':' -f 2)
|
||||
yarn build
|
||||
cd bin
|
||||
./fix-path
|
||||
cd ..
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git pull origin gh-pages
|
||||
git add .
|
||||
git commit -nm "Release $version"
|
||||
git push origin gh-pages
|
||||
@@ -1,17 +0,0 @@
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 4'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exempt-issue-labels: 'retained'
|
||||
exempt-pr-labels: 'retained'
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This pr is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
@@ -1,13 +0,0 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
.cache/
|
||||
build/
|
||||
yarn-error.log
|
||||
.npmrc
|
||||
.DS_Store
|
||||
/.svelte-kit
|
||||
/build
|
||||
/functions
|
||||
/snapshots.js
|
||||
/cypress/videos
|
||||
/cypress/screenshots
|
||||
@@ -1 +0,0 @@
|
||||
_
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run pre-commit
|
||||
@@ -1,6 +0,0 @@
|
||||
docs/**
|
||||
.svelte-kit/**
|
||||
static/**
|
||||
build/**
|
||||
node_modules/**
|
||||
snapshots.js
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"svelteSortOrder": "options-scripts-markup-styles",
|
||||
"svelteBracketNewLine": false,
|
||||
"useTabs": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the maintainers. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -1,17 +0,0 @@
|
||||
# Two-stage docker container for mermaid-js/mermaid-live-editor
|
||||
# Build : docker build -t mermaid-js/mermaid-live-editor .
|
||||
# Run : docker run --name mermaid-live-editor --publish 8080:80 mermaid-js/mermaid-live-editor
|
||||
# Start : docker start mermaid-live-editor
|
||||
# Use webbrowser : http://localhost:8080
|
||||
# Stop : press ctrl + c
|
||||
# or
|
||||
# docker stop mermaid-live-editor
|
||||
|
||||
FROM node:14.16.0-alpine3.13 as mermaid-live-editor-builder
|
||||
COPY --chown=node:node . /home
|
||||
WORKDIR /home
|
||||
RUN yarn install
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:alpine as mermaid-live-editor-runner
|
||||
COPY --from=mermaid-live-editor-builder --chown=nginx:nginx /home/docs /usr/share/nginx/html
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 - 2021 Knut Sveidqvist
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,58 +0,0 @@
|
||||
[](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE)
|
||||
|
||||
# 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
|
||||
|
||||
Edit, preview and share mermaid charts/diagrams.
|
||||
|
||||
## Features
|
||||
|
||||
- Edit and preview flowcharts, sequence diagrams, gantt diagrams in real time.
|
||||
- Save the result as a svg
|
||||
- Get a link to a viewer of the diagram so that you can share it with others.
|
||||
- Get a link to edit the diagram so that someone else can tweak it and send a new link back
|
||||
|
||||
## Live demo
|
||||
|
||||
You can try out a live version [here](https://mermaid-js.github.io/mermaid-live-editor/).
|
||||
|
||||
## Setup
|
||||
|
||||
[Volta](https://volta.sh) is used for managing node and yarn versions.
|
||||
|
||||
This project is set up using [Yarn](https://classic.yarnpkg.com/en/docs/getting-started). :
|
||||
|
||||
```
|
||||
yarn install
|
||||
```
|
||||
|
||||
or in develop branch to use the beta version of mermaid use
|
||||
|
||||
```
|
||||
yarn install --update-checksums
|
||||
```
|
||||
|
||||
This together with a .npmrc file:
|
||||
|
||||
```
|
||||
registry=https://registry.npmjs.com/
|
||||
@mermaid-js:registry=https://npm.pkg.github.com
|
||||
//npm.pkg.github.com/:_authToken="XXXXXXXXX"
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
yarn dev -- --open
|
||||
```
|
||||
|
||||
This app is created with Svelte Kit.
|
||||
|
||||
## Release
|
||||
|
||||
```
|
||||
yarn release
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Clone mermaid-js/docs parallel with the mermaid live editor.
|
||||
|
||||
# git clone git@github.com:mermaid-js/docs.git
|
||||
# git clone https://github.com/mermaid-js/docs.git
|
||||
set -e
|
||||
rm -rf docs
|
||||
yarn release
|
||||
pushd .
|
||||
if [ ! -d ../docs ]; then
|
||||
echo "Clone https://github.com/mermaid-js/docs to parent folder before continuing."
|
||||
exit
|
||||
fi
|
||||
rm -rf ../docs/mermaid-live-editor-beta/*
|
||||
cp -r docs/* ../docs/mermaid-live-editor-beta
|
||||
cd ../docs
|
||||
git add .
|
||||
git commit -m "Beta updated with latest changes"
|
||||
git push
|
||||
popd
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
pushd ../docs
|
||||
# find ./ -type f -exec sed -i 's/service-worker.js/mermaid-live-editor\/service-worker.js/g' {} \;
|
||||
find ./ -type f -exec sed -i 's/manifest.json/\/mermaid-live-editor\/manifest.json/g' {} \;
|
||||
find ./ -type f -exec sed -i 's/favicon.png/\/mermaid-live-editor\/favicon.png/g' {} \;
|
||||
popd
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"projectId": "2ckppp",
|
||||
"$schema": "https://on.cypress.io/cypress.schema.json",
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"pluginsFile": "./cypress/plugins/index.cjs",
|
||||
"viewportWidth": 1440,
|
||||
"viewportHeight": 768,
|
||||
"snapshotFileName": "./cypress/snapshots.js",
|
||||
"defaultCommandTimeout": 16000,
|
||||
"requestTimeout": 16000
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"plugins": ["cypress", "mocha"],
|
||||
"extends": ["plugin:cypress/recommended", "plugin:mocha/recommended"],
|
||||
"rules": {
|
||||
"jest/expect-expect": "off",
|
||||
"mocha/no-mocha-arrows": "off"
|
||||
},
|
||||
"env": {
|
||||
"cypress/globals": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
describe('Check actions', () => {
|
||||
// eslint-disable-next-line mocha/no-hooks-for-single-case
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
cy.contains('Actions').click();
|
||||
});
|
||||
it('should update markdown code', () => {
|
||||
cy.get('#markdown')
|
||||
.invoke('val')
|
||||
.then((oldText) => {
|
||||
cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
|
||||
cy.get('#markdown')
|
||||
.invoke('val')
|
||||
.then((newText) => {
|
||||
expect(oldText).to.not.eq(newText);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
describe('Auto sync tests', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
});
|
||||
it('should dim diagram when code is edited', () => {
|
||||
cy.contains('Auto sync').click();
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.get('#editor').type(' C --> Test');
|
||||
cy.get('#view').should('have.class', 'outOfSync');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
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');
|
||||
cy.get('#editor').type(' C --> Test');
|
||||
cy.get('#view').should('have.class', 'outOfSync');
|
||||
cy.get('[data-cy=sync]').click();
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.get('#autoSync').check();
|
||||
cy.get('#editor').type('ing');
|
||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
describe('Save History', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/edit');
|
||||
});
|
||||
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);
|
||||
cy.get('#editor').type(' 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();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.get('#historyList').find('No items in History').should('not.exist');
|
||||
cy.get('#historyList').find('li').should('have.length', 1);
|
||||
cy.contains('HistoryTest');
|
||||
cy.contains('Restore').click();
|
||||
cy.contains('HistoryTest').should('not.exist');
|
||||
cy.contains('Delete').click();
|
||||
cy.get('#historyList').find('li').should('have.length', 0);
|
||||
cy.get('#historyList').contains('No items in History');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.get('#saveHistory').click();
|
||||
cy.get('#editor').type('ing');
|
||||
cy.get('#clearHistory').click();
|
||||
cy.on('window:alert', (str) => {
|
||||
expect(str).to.equal('Clear all saved items?');
|
||||
});
|
||||
cy.on('window:confirm', () => true);
|
||||
cy.get('#historyList').contains('No items in History');
|
||||
});
|
||||
|
||||
it('should auto save history', () => {
|
||||
cy.clock();
|
||||
cy.get('#editor').type(' C --> HistoryTest');
|
||||
cy.tick(70000);
|
||||
cy.contains('Timeline').click();
|
||||
cy.get('#historyList').find('li').should('have.length', 1);
|
||||
cy.get('#editor').type('ing');
|
||||
cy.tick(70000);
|
||||
cy.get('#historyList').find('li').should('have.length', 2);
|
||||
for (let i = 0; i < 31; i++) {
|
||||
cy.get('#editor').type('.');
|
||||
cy.tick(70000);
|
||||
}
|
||||
cy.get('#historyList').find('li').should('have.length', 30);
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
describe('Site Loads', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage();
|
||||
});
|
||||
it('Check Home page load', () => {
|
||||
cy.visit('/');
|
||||
cy.url().should('include', '/edit');
|
||||
cy.contains('History').click();
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('Check Redirect from old URL', () => {
|
||||
cy.visit(
|
||||
'/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
|
||||
);
|
||||
cy.url().should(
|
||||
'include',
|
||||
'/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW0NocmlzdG1hc10gLS0-fEdldCBtb25leXwgQihHbyBzaG9wcGluZylcbiAgICBCIC0tPiBDe0xldCBtZSB0aGlua31cbiAgICBDIC0tPnxPbmV8IERbTGFwdG9wXVxuICAgIEMgLS0-fFR3b3wgRVtpUGhvbmVdXG4gICAgQyAtLT58VGhyZWV8IEZbZmE6ZmEtY2FyIENhcl0iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9LCJ1cGRhdGVFZGl0b3IiOmZhbHNlfQ'
|
||||
);
|
||||
|
||||
cy.contains('History').click();
|
||||
cy.getLocalStorage('codeStore').snapshot();
|
||||
});
|
||||
|
||||
it('should load sample diagrams when clicked', () => {
|
||||
cy.contains('Sample Diagrams').click();
|
||||
cy.contains('Pie Chart').click();
|
||||
cy.contains('pie title Pets adopted by volunteers');
|
||||
cy.contains('Class Diagram').click();
|
||||
cy.contains('classDiagram');
|
||||
});
|
||||
});
|
||||
@@ -1,31 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import * as fs from 'fs';
|
||||
|
||||
module.exports = (on, config) => {
|
||||
on('task', {
|
||||
readFileMaybe(filename) {
|
||||
if (fs.existsSync(filename)) {
|
||||
return fs.readFileSync(filename, 'utf8');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
module.exports = {
|
||||
"Site Loads": {
|
||||
"Check Home page load": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":true,\"autoSync\":true,\"updateDiagram\":true}"
|
||||
},
|
||||
"Check Redirect from old URL": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
|
||||
}
|
||||
},
|
||||
"__version": "7.3.0",
|
||||
"Auto sync tests": {
|
||||
"should dim diagram when code is edited": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
|
||||
},
|
||||
"should not dim diagram when code is in sync": {
|
||||
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Testing\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":false}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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')
|
||||
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/;
|
||||
Cypress.on('uncaught:exception', (err) => {
|
||||
/* returning false here prevents Cypress from failing the test */
|
||||
if (resizeObserverLoopErrRe.test(err.message)) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "cypress-localstorage-commands"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
|
||||
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-50d0be99.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-96e74604.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/vendor-8b597521.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/preload-helper-9f12a5fd.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/singletons-bb9012b7.js">
|
||||
@@ -67,7 +67,7 @@
|
||||
<link rel="stylesheet" href="/mermaid-live-editor/_app/assets/start-0826e215.css">
|
||||
|
||||
<script type="module">
|
||||
import { start } from "/mermaid-live-editor/_app/start-50d0be99.js";
|
||||
import { start } from "/mermaid-live-editor/_app/start-96e74604.js";
|
||||
start({
|
||||
target: document.querySelector("#svelte"),
|
||||
paths: {"base":"/mermaid-live-editor","assets":"/mermaid-live-editor"},
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
@@ -59,7 +59,7 @@
|
||||
|
||||
|
||||
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-50d0be99.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-96e74604.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/vendor-8b597521.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/preload-helper-9f12a5fd.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/singletons-bb9012b7.js">
|
||||
@@ -67,7 +67,7 @@
|
||||
<link rel="stylesheet" href="/mermaid-live-editor/_app/assets/start-0826e215.css">
|
||||
|
||||
<script type="module">
|
||||
import { start } from "/mermaid-live-editor/_app/start-50d0be99.js";
|
||||
import { start } from "/mermaid-live-editor/_app/start-96e74604.js";
|
||||
start({
|
||||
target: document.querySelector("#svelte"),
|
||||
paths: {"base":"/mermaid-live-editor","assets":"/mermaid-live-editor"},
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"name": "mermaid-live-editor",
|
||||
"version": "2.0.65",
|
||||
"scripts": {
|
||||
"dev": "svelte-kit dev",
|
||||
"build": "svelte-kit build",
|
||||
"preview": "svelte-kit preview",
|
||||
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||
"lint:fix": "prettier --write --plugin-search-dir=. . && eslint --fix --ignore-path .gitignore .",
|
||||
"format": "prettier --write --plugin-search-dir=. .",
|
||||
"pre-commit": "lint-staged",
|
||||
"postinstall": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/snapshot": "^2.1.7",
|
||||
"@sveltejs/adapter-static": "^1.0.0-next.11",
|
||||
"@sveltejs/kit": "^1.0.0-next.109",
|
||||
"@types/mermaid": "^8.2.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
||||
"@typescript-eslint/parser": "^4.19.0",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"chai": "^4.3.4",
|
||||
"cssnano": "^5.0.1",
|
||||
"cypress": "^7.3.0",
|
||||
"cypress-localstorage-commands": "^1.4.4",
|
||||
"eslint": "^7.27.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-cypress": "^2.11.3",
|
||||
"eslint-plugin-mocha": "^8.1.0",
|
||||
"eslint-plugin-postcss-modules": "^1.2.1",
|
||||
"eslint-plugin-svelte3": "^3.2.0",
|
||||
"eslint-plugin-tailwindcss": "^1.8.0",
|
||||
"husky": "^6.0.0",
|
||||
"lint-staged": "^11.0.0",
|
||||
"mocha": "^8.4.0",
|
||||
"postcss": "^8.2.10",
|
||||
"postcss-load-config": "^3.0.1",
|
||||
"prettier": "~2.2.1",
|
||||
"prettier-plugin-svelte": "^2.2.0",
|
||||
"svelte": "^3.34.0",
|
||||
"svelte-preprocess": "^4.7.1",
|
||||
"tailwindcss": "^2.1.1",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^4.0.0"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@analytics/google-analytics": "^0.5.2",
|
||||
"@macfja/svelte-persistent-store": "^1.1.0",
|
||||
"analytics": "^0.7.5",
|
||||
"js-base64": "^3.6.1",
|
||||
"mermaid": "^8.10.1",
|
||||
"moment": "^2.29.1",
|
||||
"monaco-editor": "^0.24.0",
|
||||
"random-word-slugs": "^0.0.2"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,svelte,js,css,md,json}": [
|
||||
"prettier --plugin-search-dir=. --write",
|
||||
"eslint --ignore-path .gitignore "
|
||||
]
|
||||
},
|
||||
"volta": {
|
||||
"node": "14.16.1",
|
||||
"yarn": "1.22.10",
|
||||
"npm": "7.11.2"
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const mode = process.env.NODE_ENV;
|
||||
const dev = mode === 'development';
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// Some plugins, like postcss-nested, need to run before Tailwind
|
||||
|
||||
tailwindcss,
|
||||
|
||||
// But others, like autoprefixer, need to run after
|
||||
|
||||
autoprefixer,
|
||||
|
||||
!dev &&
|
||||
cssnano({
|
||||
preset: 'default'
|
||||
})
|
||||
]
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Online FlowChart & Diagrams Editor - Mermaid Live Editor</title>
|
||||
<meta
|
||||
name="og:image"
|
||||
content="https://github.com/mermaid-js/mermaid/raw/develop/img/header.png"
|
||||
/>
|
||||
<link rel="canonical" href="https://mermaid-js.github.io/mermaid-live-editor/" />
|
||||
<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="favicon.png" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css"
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/mermaid@8.10.1/dist/mermaid.min.js"
|
||||
integrity="sha256-aQCGsx3/OLAXwNyOUrAedielIQAjIrMFkcWdXMlIkGc="
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.23.0/min/vs/editor/editor.main.min.css"
|
||||
integrity="sha512-RhnuJxgda82cBylRysVDqdpwnBcSkMzxz1+BxGwBPNMzBJlvaOjHz7P2kXELC0bdNxazsNSyJb7KAww0T5iUdQ=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<script>
|
||||
var require = {
|
||||
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.23.0/min/vs' }
|
||||
};
|
||||
</script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.23.0/min/vs/loader.min.js"
|
||||
integrity="sha512-+8+MX2hyUZxaUfMJT0ew+rPsrTGiTmCg8oksa6uVE/ZlR/g3SJtyozqcqDGkw/W785xYAvcx1LxXPP+ywD0SNw=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.23.0/min/vs/editor/editor.main.nls.min.js"
|
||||
integrity="sha512-3aAqsDS5ClTlwyzxufRhW9WMwZlYn3V8GLRl324pMIftp+CA6kvxfjlm8TG7Wi53eBcerEdX0YfXm3QLvf1Pqg=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
<script
|
||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.23.0/min/vs/editor/editor.main.js"
|
||||
integrity="sha512-sOJZBKiSvh/6vxIimdXkjO45Nd3v2n3ZYtrwktgj5SJh0+PCqNQ5oqzxEr6c/o4ULczn1Wo2yh1qrz+ghduDUA=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
></script>
|
||||
|
||||
%svelte.head%
|
||||
</head>
|
||||
<body>
|
||||
<div id="svelte">%svelte.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.nav-btn {
|
||||
@apply lg:p-2 py-3 px-0 block border-b-2 border-transparent hover:border-white;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply bg-indigo-500 hover:bg-indigo-700 rounded px-4 shadow;
|
||||
}
|
||||
.action-btn {
|
||||
@apply rounded p-2 bg-indigo-400 shadow flex-auto text-white hover:bg-indigo-500;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="@sveltejs/kit" />
|
||||
@@ -1,167 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import type { State } from '$lib/types';
|
||||
import { base64State, codeStore } from '$lib/util/state';
|
||||
import { toBase64 } from 'js-base64';
|
||||
import moment from 'moment';
|
||||
|
||||
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
||||
|
||||
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
|
||||
svg?.setAttribute('height', `${height}px`);
|
||||
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
|
||||
if (!svg) {
|
||||
svg = document.querySelector('#container svg');
|
||||
}
|
||||
const svgString = svg.outerHTML.replaceAll('<br>', '<br/>');
|
||||
return toBase64(svgString);
|
||||
};
|
||||
|
||||
const exportImage = (event: Event, exporter: Exporter) => {
|
||||
const canvas: HTMLCanvasElement = document.createElement('canvas');
|
||||
const svg: HTMLElement = document.querySelector('#container svg');
|
||||
const box: DOMRect = svg.getBoundingClientRect();
|
||||
canvas.width = box.width;
|
||||
canvas.height = box.height;
|
||||
if (imagemodeselected === 'width') {
|
||||
const ratio = box.height / box.width;
|
||||
canvas.width = userimagesize;
|
||||
canvas.height = userimagesize * ratio;
|
||||
} else if (imagemodeselected === 'height') {
|
||||
const ratio = box.width / box.height;
|
||||
canvas.width = userimagesize * ratio;
|
||||
canvas.height = userimagesize;
|
||||
}
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.fillStyle = 'white';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
const image = new Image();
|
||||
image.onload = exporter(context, image);
|
||||
image.src = `data:image/svg+xml;base64,${getBase64SVG(svg, canvas.width, canvas.height)}`;
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const simulateDownload = (download: string, href: string): void => {
|
||||
const a = document.createElement('a');
|
||||
a.download = download;
|
||||
a.href = href;
|
||||
a.click();
|
||||
a.remove();
|
||||
};
|
||||
const downloadImage: Exporter = (context, image) => {
|
||||
return () => {
|
||||
const { canvas } = context;
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
simulateDownload(
|
||||
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.png`,
|
||||
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const isClipboardAvailable = (): boolean => {
|
||||
return Object.prototype.hasOwnProperty.call(window, 'ClipboardItem') as boolean;
|
||||
};
|
||||
|
||||
const clipboardCopy: Exporter = (context, image) => {
|
||||
return () => {
|
||||
const { canvas } = context;
|
||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||
canvas.toBlob((blob) => {
|
||||
try {
|
||||
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
|
||||
navigator.clipboard.write([
|
||||
/* eslint-disable no-undef */
|
||||
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
|
||||
new ClipboardItem({
|
||||
[blob.type]: blob
|
||||
})
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onCopyClipboard = (event: Event) => {
|
||||
exportImage(event, clipboardCopy);
|
||||
};
|
||||
|
||||
const onDownloadPNG = (event: Event) => {
|
||||
exportImage(event, downloadImage);
|
||||
};
|
||||
|
||||
const onDownloadSVG = () => {
|
||||
simulateDownload(
|
||||
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.svg`,
|
||||
`data:image/svg+xml;base64,${getBase64SVG()}`
|
||||
);
|
||||
};
|
||||
|
||||
const onCopyMarkdown = (event: Event) => {
|
||||
event.target.select();
|
||||
document.execCommand('Copy');
|
||||
};
|
||||
|
||||
let iUrl: string;
|
||||
let svgUrl: string;
|
||||
let mdCode: string;
|
||||
let imagemodeselected = 'auto';
|
||||
let userimagesize = 1080;
|
||||
|
||||
codeStore.subscribe((state: State) => {
|
||||
const stateCopy = JSON.parse(JSON.stringify(state));
|
||||
if (typeof stateCopy.mermaid === 'string') {
|
||||
stateCopy.mermaid = JSON.parse(stateCopy.mermaid);
|
||||
}
|
||||
const b64Code = toBase64(JSON.stringify(stateCopy), true);
|
||||
iUrl = `https://mermaid.ink/img/${b64Code}`;
|
||||
svgUrl = `https://mermaid.ink/svg/${b64Code}`;
|
||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${window.location.hash})`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<Card title="Actions" isOpen={false}>
|
||||
<div class="flex flex-wrap gap-2 m-2">
|
||||
{#if isClipboardAvailable()}
|
||||
<button class="action-btn w-full" on:click={onCopyClipboard}
|
||||
><i class="far fa-copy" /> Copy Image to clipboard
|
||||
</button>
|
||||
{/if}
|
||||
<button class="action-btn flex-auto" on:click={onDownloadPNG}>
|
||||
<i class="fas fa-download" /> PNG
|
||||
</button>
|
||||
<button class="action-btn flex-auto" on:click={onDownloadSVG}>
|
||||
<i class="fas fa-download" /> SVG
|
||||
</button>
|
||||
<button class="action-btn flex-auto">
|
||||
<a target="_blank" href={iUrl}><i class="fas fa-external-link-alt" /> PNG</a>
|
||||
</button>
|
||||
<button class="action-btn flex-auto">
|
||||
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt" /> SVG</a>
|
||||
</button>
|
||||
|
||||
<div class="flex gap-2 items-center">
|
||||
PNG size
|
||||
<input type="radio" value="auto" id="autosize" bind:group={imagemodeselected} />
|
||||
<label for="autosize">Auto</label>
|
||||
<input type="radio" value="width" id="width-active" bind:group={imagemodeselected} />
|
||||
<label for="width">Width</label>
|
||||
<input type="radio" value="height" id="height-active" bind:group={imagemodeselected} />
|
||||
<label for="height">Height</label>
|
||||
{#if imagemodeselected !== 'auto'}
|
||||
<input id="height" type="number" min="3" max="10000" bind:value={userimagesize} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="w-full flex gap-2 items-center">
|
||||
<label for="markdown">Copy Markdown</label>
|
||||
<input class="flex-1" id="markdown" type="text" value={mdCode} on:click={onCopyMarkdown} />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
@@ -1,26 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Tab } from '$lib/types';
|
||||
import { slide } from 'svelte/transition';
|
||||
import Tabs from './tabs.svelte';
|
||||
export let isCloseable = true;
|
||||
export let isOpen = true;
|
||||
export let tabs: Tab[] = [];
|
||||
export let title: string;
|
||||
$: isOpen = isCloseable ? isOpen : true;
|
||||
</script>
|
||||
|
||||
<div class="bg-white rounded overflow-hidden shadow m-2 flex-grow flex flex-col">
|
||||
<div class="bg-indigo-400 border-gray-400 p-2 flex-none" on:click={() => (isOpen = !isOpen)}>
|
||||
<div class="flex justify-between">
|
||||
<Tabs on:select {tabs} bind:isOpen {title} {isCloseable} />
|
||||
<div class="flex gap-x-4 items-center text-white">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if isOpen}
|
||||
<div class="flex-grow overflow-auto" transition:slide>
|
||||
<slot />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,51 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { Tab, TabEvents } from '$lib/types';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
export let isCloseable = true;
|
||||
export let tabs: Tab[] = [];
|
||||
export let title: string;
|
||||
export let isOpen = false;
|
||||
|
||||
let activeTabID: string = tabs[0]?.id;
|
||||
|
||||
const dispatch = createEventDispatcher<TabEvents>();
|
||||
const toggleTabs = (tab: Tab) => {
|
||||
activeTabID = tab.id;
|
||||
dispatch('select', tab);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex cursor-default">
|
||||
<span class="text-white mr-2 font-semibold" on:click|stopPropagation={() => (isOpen = !isOpen)}>
|
||||
{#if isCloseable}
|
||||
<i class="fas fa-chevron-right icon" class:isOpen />
|
||||
{/if}
|
||||
{title}</span>
|
||||
{#if isOpen && tabs}
|
||||
<ul class="flex flex-wrap flex-row" transition:fade>
|
||||
{#each tabs as tab}
|
||||
<li class="mr-2 last:mr-0 w-28 h-6 flex-auto text-center">
|
||||
<div
|
||||
class="text cursor-pointer font-semibold min-w-16 w-auto px-2 py-1 -mb-4 rounded-t block leading-normal {activeTabID ===
|
||||
tab.id
|
||||
? 'text-indigo-500 bg-white border-white'
|
||||
: 'text-white bg-indigo-500 border-indigo-500 hover:bg-indigo-600'}"
|
||||
on:click|stopPropagation={() => toggleTabs(tab)}>
|
||||
<i class={tab.icon} />
|
||||
{tab.title}
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
.isOpen {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
@@ -1,76 +0,0 @@
|
||||
<script lang="ts">
|
||||
import type { EditorEvents } from '$lib/types';
|
||||
import { codeStore } from '$lib/util/state';
|
||||
import type monaco from 'monaco-editor';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { initEditor } from './util';
|
||||
|
||||
let divEl: HTMLDivElement = null;
|
||||
let editor: monaco.editor.IStandaloneCodeEditor;
|
||||
let Monaco;
|
||||
|
||||
export let text: string;
|
||||
export let language: string;
|
||||
export let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
|
||||
value: text,
|
||||
language: language,
|
||||
scrollBeyondLastLine: false,
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
theme: 'myCoolTheme',
|
||||
overviewRulerLanes: 0
|
||||
};
|
||||
export let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||
let oldText = text;
|
||||
$: Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||
$: {
|
||||
if (text !== oldText) {
|
||||
if ($codeStore.updateEditor) {
|
||||
editor?.setValue(text);
|
||||
}
|
||||
oldText = text;
|
||||
}
|
||||
Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<EditorEvents>();
|
||||
const loadMonaco = async () => {
|
||||
let i = 0;
|
||||
while (i++ < 10) {
|
||||
try {
|
||||
//@ts-ignore : This is a hack to handle a svelte-kit error when importing monaco.
|
||||
Monaco = monaco;
|
||||
return;
|
||||
} catch {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
}
|
||||
alert('Loading Monaco Editor failed. Please try refreshing the page.');
|
||||
};
|
||||
onMount(async () => {
|
||||
await loadMonaco(); // Fix https://github.com/mermaid-js/mermaid-live-editor/issues/175
|
||||
initEditor(Monaco);
|
||||
editor = Monaco.editor.create(divEl, editorOptions);
|
||||
editor.onDidChangeModelContent(() => {
|
||||
text = editor.getValue();
|
||||
dispatch('update', {
|
||||
text
|
||||
});
|
||||
});
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
editor.layout({
|
||||
height: entries[0].contentRect.height,
|
||||
width: entries[0].contentRect.width
|
||||
});
|
||||
});
|
||||
|
||||
resizeObserver.observe(divEl.parentElement);
|
||||
return () => {
|
||||
editor.dispose();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div bind:this={divEl} id="editor" class="overflow-hidden" />
|
||||
@@ -1,77 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const initEditor = (monacoEditor): void => {
|
||||
monacoEditor.languages.register({ id: 'mermaid' });
|
||||
|
||||
// Register a tokens provider for the language
|
||||
monacoEditor.languages.setMonarchTokensProvider('mermaid', {
|
||||
typeKeywords: [
|
||||
'graph',
|
||||
'stateDiagram',
|
||||
'sequenceDiagram',
|
||||
'classDiagram',
|
||||
'pie',
|
||||
'erDiagram',
|
||||
'flowchart',
|
||||
'gantt',
|
||||
'gitGraph',
|
||||
'journey'
|
||||
],
|
||||
keywords: ['patricipant', 'as'],
|
||||
arrows: ['---', '===', '-->', '==>', '->>', '->'],
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/[{}]/, 'delimiter.bracket'],
|
||||
[/[a-z_$][\w$]*/, { cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } }],
|
||||
[/[-=>ox]+/, { cases: { '@arrows': 'transition' } }],
|
||||
[/[[{(}]+.+?[)\]}]+/, 'string'],
|
||||
[/".*"/, 'string']
|
||||
]
|
||||
},
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, 'white'],
|
||||
[/%%.*$/, 'comment']
|
||||
]
|
||||
});
|
||||
|
||||
monacoEditor.editor.defineTheme('myCoolTheme', {
|
||||
base: 'vs',
|
||||
inherit: false,
|
||||
rules: [
|
||||
{ token: 'keyword', foreground: '880000', fontStyle: 'bold' },
|
||||
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
|
||||
{ token: 'string', foreground: 'AA8500' },
|
||||
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
|
||||
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' }
|
||||
]
|
||||
});
|
||||
|
||||
// Register a completion item provider for the new language
|
||||
monacoEditor.languages.registerCompletionItemProvider('mermaid', {
|
||||
provideCompletionItems: () => {
|
||||
const suggestions = [
|
||||
{
|
||||
label: 'simpleText',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Text,
|
||||
insertText: 'simpleText'
|
||||
},
|
||||
{
|
||||
label: 'testing',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Keyword,
|
||||
insertText: 'testing(${1:condition})',
|
||||
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet
|
||||
},
|
||||
{
|
||||
label: 'ifelse',
|
||||
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||
insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
|
||||
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
documentation: 'If-Else Statement'
|
||||
}
|
||||
];
|
||||
return { suggestions: suggestions };
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,117 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import { codeStore, getStateString } from '$lib/util/state';
|
||||
import {
|
||||
addHistoryEntry,
|
||||
autoHistoryMode,
|
||||
clearHistoryData,
|
||||
getPreviousState,
|
||||
historyStore
|
||||
} from './history';
|
||||
import { notify, prompt } from '$lib/util/notify';
|
||||
import { onMount } from 'svelte';
|
||||
import moment from 'moment';
|
||||
import type { State, Tab } from '$lib/types';
|
||||
|
||||
const HISTORY_SAVE_INTERVAL = 60000;
|
||||
|
||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||
autoHistoryMode.set('timeline' === message.detail.id);
|
||||
};
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: 'saved',
|
||||
title: 'Saved',
|
||||
icon: 'far fa-bookmark'
|
||||
},
|
||||
{
|
||||
id: 'timeline',
|
||||
title: 'Timeline',
|
||||
icon: 'fas fa-history'
|
||||
}
|
||||
];
|
||||
|
||||
const saveHistory = (auto = false) => {
|
||||
const currentState: string = getStateString();
|
||||
const previousState: string = getPreviousState(auto);
|
||||
if (previousState !== currentState) {
|
||||
addHistoryEntry({
|
||||
state: $codeStore,
|
||||
time: Date.now(),
|
||||
auto
|
||||
});
|
||||
} else if (!auto) {
|
||||
notify('State already saved.');
|
||||
}
|
||||
};
|
||||
|
||||
const clearHistory = (date?: number): void => {
|
||||
if (!date && !prompt('Clear all saved items?')) {
|
||||
return;
|
||||
}
|
||||
clearHistoryData(date);
|
||||
};
|
||||
|
||||
const restoreHistory = (state: State): void => {
|
||||
codeStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||
};
|
||||
|
||||
const relativeTime = (time: number) => {
|
||||
const t = new Date(time);
|
||||
return `${new Date(t).toLocaleString()} (${moment(t).fromNow()})`;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
autoHistoryMode.set(false);
|
||||
setInterval(() => {
|
||||
saveHistory(true);
|
||||
}, HISTORY_SAVE_INTERVAL);
|
||||
});
|
||||
|
||||
let isOpen = true;
|
||||
</script>
|
||||
|
||||
<Card on:select={tabSelectHandler} bind:isOpen {tabs} title="History">
|
||||
<div slot="actions">
|
||||
<button
|
||||
id="saveHistory"
|
||||
class="btn"
|
||||
on:click|stopPropagation={() => saveHistory()}
|
||||
title="Save current state"><i class="far fa-save" /></button>
|
||||
<button
|
||||
id="clearHistory"
|
||||
class="btn text-red-400"
|
||||
on:click|stopPropagation={() => clearHistory()}
|
||||
title="Delete all saved states"><i class="fas fa-trash-alt" /></button>
|
||||
</div>
|
||||
<ul class="p-2 space-y-2 overflow-auto h-56" id="historyList">
|
||||
{#if $historyStore.length > 0}
|
||||
{#each $historyStore as { state, time, name }}
|
||||
<li class="rounded p-2 shadow flex-col">
|
||||
<div class="flex">
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-col">
|
||||
<span>{name}</span>
|
||||
<span class="text-gray-400 text-sm">{relativeTime(time)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-2 content-center">
|
||||
<button
|
||||
class="rounded px-2 w-24 bg-green-200 hover:bg-green-300"
|
||||
on:click={() => restoreHistory(state)}><i class="fas fa-undo" /> Restore</button>
|
||||
<button
|
||||
class="rounded px-2 w-24 bg-red-200 hover:bg-red-300"
|
||||
on:click={() => clearHistory(time)}><i class="fas fa-trash-alt" /> Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="m-2 text-gray-600">
|
||||
No items in History<br />
|
||||
Click the Save button to save current state and restore it later.<br />
|
||||
Timeline will automatically be saved every minute.
|
||||
</div>
|
||||
{/if}
|
||||
</ul>
|
||||
</Card>
|
||||
@@ -1,60 +0,0 @@
|
||||
import { derived, Readable, Writable, writable, get } from 'svelte/store';
|
||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||
import { generateSlug } from 'random-word-slugs';
|
||||
import type { HistoryEntry } from '../../types';
|
||||
|
||||
const MAX_AUTO_HISTORY_LENGTH = 30;
|
||||
|
||||
export const autoHistoryMode: Writable<boolean> = persist(
|
||||
writable(true),
|
||||
localStorage(),
|
||||
'autoHistoryMode'
|
||||
);
|
||||
|
||||
const autoHistoryStore: Writable<HistoryEntry[]> = persist(
|
||||
writable([]),
|
||||
localStorage(),
|
||||
'autoHistoryStore'
|
||||
);
|
||||
|
||||
const manualHistoryStore: Writable<HistoryEntry[]> = persist(
|
||||
writable([]),
|
||||
localStorage(),
|
||||
'manualHistoryStore'
|
||||
);
|
||||
|
||||
export const historyStore: Readable<HistoryEntry[]> = derived(
|
||||
[autoHistoryMode, autoHistoryStore, manualHistoryStore],
|
||||
([autoMode, autoHistories, manualHistories], set) => {
|
||||
set(autoMode ? autoHistories : manualHistories);
|
||||
}
|
||||
);
|
||||
|
||||
export const addHistoryEntry = (entry: HistoryEntry): void => {
|
||||
entry.name = generateSlug(2);
|
||||
|
||||
if (!entry.auto) {
|
||||
manualHistoryStore.update((entries) => [entry, ...entries]);
|
||||
return;
|
||||
}
|
||||
autoHistoryStore.update((entries) => {
|
||||
if (entries.length === MAX_AUTO_HISTORY_LENGTH) {
|
||||
entries.pop();
|
||||
}
|
||||
return [entry, ...entries];
|
||||
});
|
||||
};
|
||||
|
||||
export const clearHistoryData = (time?: number): void => {
|
||||
(get(autoHistoryMode) ? autoHistoryStore : manualHistoryStore).update((entries) =>
|
||||
entries.filter((entry) => time && entry.time != time)
|
||||
);
|
||||
};
|
||||
|
||||
export const getPreviousState = (auto: boolean): string => {
|
||||
const entries = get(auto ? autoHistoryStore : manualHistoryStore);
|
||||
if (entries.length > 0) {
|
||||
return JSON.stringify(entries[0].state);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@@ -1,80 +0,0 @@
|
||||
<script context="module">
|
||||
import { version } from 'mermaid/package.json';
|
||||
import { analytics } from '$lib/util/stats';
|
||||
analytics?.track('version', {
|
||||
mermaidVersion: version
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
interface Link {
|
||||
title: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
}
|
||||
const links: Link[] = [
|
||||
{
|
||||
title: 'Documentation',
|
||||
href: 'https://mermaid-js.github.io/mermaid/#/n00b-gettingStarted'
|
||||
},
|
||||
{
|
||||
title: 'Tutorial',
|
||||
href: 'https://github.com/mermaid-js/mermaid/blob/develop/docs/Tutorials.md'
|
||||
},
|
||||
{
|
||||
title: 'Mermaid',
|
||||
href: 'https://github.com/mermaid-js/mermaid'
|
||||
},
|
||||
{
|
||||
title: 'CLI',
|
||||
href: 'https://github.com/mermaid-js/mermaid-cli'
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
href: 'https://github.com/mermaid-js/mermaid-live-editor',
|
||||
icon: 'fab fa-github fa-lg'
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<header class="p-2 bg-indigo-400 text-white font-bold flex flex-wrap items-center">
|
||||
<div class="flex-1 flex justify-between items-center">
|
||||
<a href="/">Mermaid<span class="text-xs font-thin">v{version}</span> Live Editor</a>
|
||||
</div>
|
||||
|
||||
<label for="menu-toggle" class="pointer-cursor lg:hidden block"
|
||||
><svg
|
||||
class="fill-current "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
><title>menu</title><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" /></svg
|
||||
></label>
|
||||
<input class="hidden" type="checkbox" id="menu-toggle" />
|
||||
|
||||
<div class="hidden lg:flex lg:items-center lg:w-auto w-full" id="menu">
|
||||
<nav>
|
||||
<ul class="lg:flex items-center justify-between text-base pt-4 lg:pt-0">
|
||||
{#each links as { title, href, icon }}
|
||||
<li>
|
||||
<a class="nav-btn" target="_blank" {href}>
|
||||
{#if icon}
|
||||
<i class={icon} />
|
||||
{/if}
|
||||
{title}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
#menu-toggle:checked + #menu {
|
||||
display: block;
|
||||
}
|
||||
header {
|
||||
z-index: 10000;
|
||||
}
|
||||
</style>
|
||||
@@ -1,114 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { updateCode } from '$lib/util/state';
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
|
||||
const samples = {
|
||||
'Flow Chart': `graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]`,
|
||||
'Sequence Diagram': `sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!
|
||||
`,
|
||||
'Class Diagram': `classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}
|
||||
`,
|
||||
'State Diagram': `stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
`,
|
||||
'Gantt Chart': `gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2014-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d
|
||||
`,
|
||||
'Pie Chart': `pie title Pets adopted by volunteers
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15
|
||||
`,
|
||||
'ER Diagram': `erDiagram
|
||||
CUSTOMER }|..|{ DELIVERY-ADDRESS : has
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
CUSTOMER ||--o{ INVOICE : "liable for"
|
||||
DELIVERY-ADDRESS ||--o{ ORDER : receives
|
||||
INVOICE ||--|{ ORDER : covers
|
||||
ORDER ||--|{ ORDER-ITEM : includes
|
||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
||||
`,
|
||||
'Git Graph': `gitGraph:
|
||||
options
|
||||
{
|
||||
"nodeSpacing": 150,
|
||||
"nodeRadius": 10
|
||||
}
|
||||
end
|
||||
commit
|
||||
branch newbranch
|
||||
checkout newbranch
|
||||
commit
|
||||
commit
|
||||
checkout master
|
||||
commit
|
||||
commit
|
||||
merge newbranch
|
||||
|
||||
`,
|
||||
'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
|
||||
`
|
||||
};
|
||||
|
||||
const loadSampleDiagram = (diagramType: string): void => {
|
||||
updateCode(samples[diagramType], true, true);
|
||||
};
|
||||
</script>
|
||||
|
||||
<Card title="Sample Diagrams" isOpen={false}>
|
||||
<div class="flex gap-2 flex-wrap p-2">
|
||||
{#each Object.keys(samples) as sample}
|
||||
<button class="action-btn" on:click={() => loadSampleDiagram(sample)}>{sample}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</Card>
|
||||
@@ -1,68 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { errorStore } from '$lib/util/error';
|
||||
|
||||
import { codeStore } from '$lib/util/state';
|
||||
import { onMount } from 'svelte';
|
||||
import type { Mermaid } from 'mermaid';
|
||||
|
||||
const mermaid: Mermaid = (window.mermaid as unknown) as Mermaid;
|
||||
let code = '';
|
||||
let container: HTMLDivElement;
|
||||
let error = false;
|
||||
let outOfSync = false;
|
||||
let manualUpdate = true;
|
||||
onMount(() => {
|
||||
codeStore.subscribe((state) => {
|
||||
try {
|
||||
if (container && state && (state.updateDiagram || state.autoSync)) {
|
||||
if (!state.autoSync) {
|
||||
$codeStore.updateDiagram = false;
|
||||
}
|
||||
outOfSync = false;
|
||||
manualUpdate = true;
|
||||
// Replacing special characters '<' and '>' with encoded '<' and '>'
|
||||
code = state.code; //.replace(/</g, '<').replace(/>/g, '>');
|
||||
const scroll = container.parentElement.parentElement.parentElement.scrollTop;
|
||||
container.innerHTML = code;
|
||||
delete container.dataset.processed;
|
||||
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
||||
mermaid.render('graph-div', code, (svgCode) => {
|
||||
container.innerHTML = svgCode;
|
||||
});
|
||||
container.parentElement.parentElement.parentElement.scrollTop = scroll;
|
||||
error = false;
|
||||
} else if (manualUpdate) {
|
||||
manualUpdate = false;
|
||||
} else {
|
||||
outOfSync = true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('view fail', e);
|
||||
error = true;
|
||||
}
|
||||
});
|
||||
errorStore.subscribe((err) => {
|
||||
if (typeof err === 'undefined') {
|
||||
error = false;
|
||||
} else {
|
||||
error = true;
|
||||
console.log('Error: ', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="view" class="p-2" class:error class:outOfSync>
|
||||
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#view {
|
||||
border: 1px solor darkred;
|
||||
flex: 1;
|
||||
}
|
||||
.error,
|
||||
.outOfSync {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Can be made globally available by placing this
|
||||
* inside `global.d.ts` and removing `export` keyword
|
||||
*/
|
||||
export interface Locals {
|
||||
userid: string;
|
||||
}
|
||||
|
||||
export interface EditorUpdateEvent {
|
||||
text: string;
|
||||
}
|
||||
export interface EditorEvents {
|
||||
update: EditorUpdateEvent;
|
||||
}
|
||||
|
||||
export interface TabEvents {
|
||||
select: Tab;
|
||||
}
|
||||
|
||||
export interface Tab {
|
||||
id: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export interface State {
|
||||
code: string;
|
||||
mermaid: string;
|
||||
updateEditor: boolean;
|
||||
updateDiagram: boolean;
|
||||
autoSync: boolean;
|
||||
}
|
||||
|
||||
export interface HistoryEntry {
|
||||
state: State;
|
||||
time: number;
|
||||
name?: string;
|
||||
auto: boolean;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const errorStore = writable(undefined);
|
||||
@@ -1,7 +0,0 @@
|
||||
export const notify = (message: string): void => {
|
||||
alert(message);
|
||||
};
|
||||
|
||||
export const prompt = (message: string): boolean => {
|
||||
return confirm(message);
|
||||
};
|
||||
@@ -1,93 +0,0 @@
|
||||
import { writable, get, derived } from 'svelte/store';
|
||||
import { toBase64, fromBase64 } from 'js-base64';
|
||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||
import type { State } from '$lib/types';
|
||||
import { saveStatistcs } from './stats';
|
||||
|
||||
const defaultState: State = {
|
||||
code: `graph TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]
|
||||
`,
|
||||
mermaid: JSON.stringify(
|
||||
{
|
||||
theme: 'default'
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
updateEditor: false,
|
||||
autoSync: true,
|
||||
updateDiagram: true
|
||||
};
|
||||
|
||||
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||
export const base64State = derived([codeStore], ([code], set) => {
|
||||
set(toBase64(JSON.stringify(code), true));
|
||||
});
|
||||
|
||||
export const loadState = (data: string): void => {
|
||||
let state: State;
|
||||
try {
|
||||
const stateStr = fromBase64(data);
|
||||
console.log(`Tring to load state: ${stateStr}`);
|
||||
state = JSON.parse(stateStr);
|
||||
if (typeof state.mermaid !== 'string') {
|
||||
state.mermaid = JSON.stringify(state.mermaid, null, 2);
|
||||
}
|
||||
} catch (e) {
|
||||
if (data) {
|
||||
console.error('Init error', e);
|
||||
}
|
||||
state = get(codeStore);
|
||||
console.log(state);
|
||||
}
|
||||
updateCodeStore({ ...state, updateEditor: true });
|
||||
};
|
||||
|
||||
export const updateCodeStore = (newState: State): void => {
|
||||
codeStore.update((state) => {
|
||||
return { ...state, ...newState };
|
||||
});
|
||||
};
|
||||
|
||||
let prompted = false;
|
||||
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
|
||||
saveStatistcs(code);
|
||||
const lines = (code.match(/\n/g) || '').length + 1;
|
||||
|
||||
if (lines > 50 && !prompted && get(codeStore).autoSync) {
|
||||
const turnOff = confirm(
|
||||
'Long diagram deteced. Turn off Auto Sync? Click the sync logo to manually sync.'
|
||||
);
|
||||
prompted = true;
|
||||
if (turnOff) {
|
||||
updateCodeStore({
|
||||
autoSync: false
|
||||
} as State);
|
||||
}
|
||||
}
|
||||
|
||||
codeStore.update((state) => {
|
||||
return { ...state, code, updateEditor, updateDiagram };
|
||||
});
|
||||
};
|
||||
|
||||
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
||||
codeStore.update((state) => {
|
||||
return { ...state, mermaid: config, updateEditor };
|
||||
});
|
||||
};
|
||||
|
||||
export const initURLSubscription = (): void => {
|
||||
base64State.subscribe((state: string) => {
|
||||
history.replaceState(undefined, undefined, `#${state}`);
|
||||
});
|
||||
};
|
||||
|
||||
export const getStateString = (): string => {
|
||||
return JSON.stringify(get(codeStore));
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
import { browser } from '$app/env';
|
||||
import type { AnalyticsInstance } from 'analytics';
|
||||
|
||||
export let analytics: AnalyticsInstance;
|
||||
|
||||
export const initAnalytics = async (): Promise<void> => {
|
||||
if (browser && !analytics) {
|
||||
try {
|
||||
const { Analytics } = await import('analytics');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const googleAnalytics = await import('@analytics/google-analytics');
|
||||
analytics = Analytics({
|
||||
app: 'mermaid-live-editor',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
plugins: [
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||
googleAnalytics.init({
|
||||
trackingId: 'UA-153180559-1'
|
||||
})
|
||||
]
|
||||
});
|
||||
} catch {
|
||||
console.info('Analytics blocked ;)');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const detectType = (text: string): string => {
|
||||
return text
|
||||
.replace(/^\s*%%.*\n/g, '\n')
|
||||
.trimStart()
|
||||
.split(' ')[0];
|
||||
};
|
||||
|
||||
// manual debounce
|
||||
let timeout;
|
||||
export const saveStatistcs = (graph: string): void => {
|
||||
if (analytics) {
|
||||
clearTimeout(timeout);
|
||||
// Only save statistcs after a 5 sec delay
|
||||
timeout = setTimeout(function () {
|
||||
const graphType = detectType(graph);
|
||||
console.debug('ga:', 'send', 'event', 'render', graphType);
|
||||
void analytics.track('render', {
|
||||
graphType
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import type { State } from '$lib/types';
|
||||
import { initURLSubscription, loadState, updateCodeStore } from './state';
|
||||
import { analytics, initAnalytics } from './stats';
|
||||
|
||||
export const loadStateFromURL = (): void => {
|
||||
loadState(window.location.hash.slice(1));
|
||||
};
|
||||
|
||||
export const syncDiagram = (): void => {
|
||||
updateCodeStore({
|
||||
updateDiagram: true
|
||||
} as State);
|
||||
};
|
||||
|
||||
export const initHandler = async (): Promise<void> => {
|
||||
loadStateFromURL();
|
||||
syncDiagram();
|
||||
initURLSubscription();
|
||||
await initAnalytics();
|
||||
analytics?.page();
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
<script lang="ts">
|
||||
import '../app.postcss';
|
||||
import { base } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
// This can be removed once https://github.com/sveltejs/kit/issues/1612 is fixed.
|
||||
// Then move it into src and vite will bundle it automatically.
|
||||
onMount(() => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register(`${base}/service-worker.js`, {
|
||||
scope: `${base}/`
|
||||
})
|
||||
.then(function (registration) {
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<main class="h-screen">
|
||||
<slot />
|
||||
</main>
|
||||
@@ -1,202 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Editor from '$lib/components/editor/editor.svelte';
|
||||
import Navbar from '$lib/components/navbar.svelte';
|
||||
import Preset from '$lib/components/preset.svelte';
|
||||
import Actions from '$lib/components/actions.svelte';
|
||||
import View from '$lib/components/view.svelte';
|
||||
import Card from '$lib/components/card/card.svelte';
|
||||
import History from '$lib/components/history/history.svelte';
|
||||
import { updateCode, updateConfig, codeStore, base64State } from '$lib/util/state';
|
||||
import { initHandler, syncDiagram } from '$lib/util/util';
|
||||
import { errorStore } from '$lib/util/error';
|
||||
import { onMount } from 'svelte';
|
||||
import type monaco from 'monaco-editor';
|
||||
import type { Mermaid } from 'mermaid';
|
||||
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
const mermaid: Mermaid = (window.mermaid as unknown) as Mermaid;
|
||||
|
||||
let selectedMode = 'code';
|
||||
const languageMap = {
|
||||
code: 'mermaid',
|
||||
config: 'json'
|
||||
};
|
||||
let text = '';
|
||||
let language: 'mermaid' | 'json' = 'mermaid';
|
||||
let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||
$: language = languageMap[selectedMode];
|
||||
$: {
|
||||
if (selectedMode === 'code') {
|
||||
text = $codeStore.code;
|
||||
} else {
|
||||
text = $codeStore.mermaid;
|
||||
}
|
||||
}
|
||||
|
||||
codeStore.subscribe((state: State) => {
|
||||
if (state.updateEditor) {
|
||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||
}
|
||||
});
|
||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||
$codeStore.updateEditor = true;
|
||||
selectedMode = message.detail.id;
|
||||
};
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: 'code',
|
||||
title: 'Code',
|
||||
icon: 'fas fa-code'
|
||||
},
|
||||
{
|
||||
id: 'config',
|
||||
title: 'Config',
|
||||
icon: 'fas fa-cogs'
|
||||
}
|
||||
];
|
||||
|
||||
const handleCodeUpdate = (code: string): void => {
|
||||
mermaid.parse(code);
|
||||
updateCode(code, false);
|
||||
};
|
||||
|
||||
const handleConfigUpdate = (config: string): void => {
|
||||
JSON.parse(config);
|
||||
updateConfig(config, false);
|
||||
};
|
||||
|
||||
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
|
||||
try {
|
||||
if (selectedMode === 'code') {
|
||||
handleCodeUpdate(message.detail.text);
|
||||
} else {
|
||||
handleConfigUpdate(message.detail.text);
|
||||
}
|
||||
errorStore.set(undefined);
|
||||
errorMarkers = [];
|
||||
} catch (e) {
|
||||
errorStore.set(e);
|
||||
if (e.hash) {
|
||||
const marker: monaco.editor.IMarkerData = {
|
||||
severity: 8, //Error
|
||||
startLineNumber: e.hash.loc.first_line,
|
||||
startColumn: e.hash.loc.first_column,
|
||||
endLineNumber: e.hash.loc.last_line,
|
||||
endColumn: e.hash.loc.last_column + 1,
|
||||
message: e.str
|
||||
};
|
||||
errorMarkers.push(marker);
|
||||
// Clear all previous errors before this error.
|
||||
errorMarkers = errorMarkers.filter(
|
||||
(m) => m.startLineNumber >= marker.startLineNumber && m.startColumn >= marker.startColumn
|
||||
);
|
||||
}
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const viewDiagram = () => {
|
||||
window.open(`${base}/view#${$base64State}`, '_blank').focus();
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
await initHandler();
|
||||
const resizer = document.getElementById('resizeHandler');
|
||||
const element = document.getElementById('editorPane');
|
||||
const resize = (e) => {
|
||||
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
||||
if (newWidth > 50) {
|
||||
element.style.width = `${newWidth}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const stopResize = () => {
|
||||
window.removeEventListener('mousemove', resize);
|
||||
};
|
||||
resizer.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
window.addEventListener('mousemove', resize);
|
||||
window.addEventListener('mouseup', stopResize);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col overflow-hidden bg-gray-100">
|
||||
<Navbar />
|
||||
<div class="flex-1 flex overflow-hidden">
|
||||
<div class="hidden md:flex flex-col" id="editorPane" style="width: 40%">
|
||||
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
|
||||
<div slot="actions">
|
||||
{#if !$codeStore.autoSync}
|
||||
<button
|
||||
class="bg-indigo-500 hover:bg-indigo-700 rounded px-4 mx-2"
|
||||
title="Sync Diagram"
|
||||
data-cy="sync"
|
||||
on:click={syncDiagram}><i class="fas fa-sync" /></button>
|
||||
{/if}
|
||||
<label
|
||||
for="autoSync"
|
||||
style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
|
||||
<input type="checkbox" id="autoSync" bind:checked={$codeStore.autoSync} />
|
||||
Auto sync
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
|
||||
</Card>
|
||||
|
||||
<div class="-mt-2">
|
||||
<Preset />
|
||||
<History />
|
||||
<Actions />
|
||||
</div>
|
||||
</div>
|
||||
<div id="resizeHandler" />
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<Card title="Diagram" isCloseable={false}>
|
||||
<button
|
||||
slot="actions"
|
||||
class="btn"
|
||||
title="View diagram in new page"
|
||||
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye" /> View</button>
|
||||
|
||||
<div class="flex-1 overflow-auto">
|
||||
<View />
|
||||
</div>
|
||||
</Card>
|
||||
<div class="md:hidden bg-white rounded shadow p-2 mx-2">
|
||||
Code editing not supported on mobile. Please use a desktop browser.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#resizeHandler {
|
||||
cursor: col-resize;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
#resizeHandler::after {
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: #ccc;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
#resizeHandler:hover::after {
|
||||
margin-left: -2px;
|
||||
background-color: #818cf8;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#resizeHandler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,17 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
onMount(async () => {
|
||||
// Handle old live editor links and redirect to new version
|
||||
const hash = window.location.hash.split('/');
|
||||
let newURL = 'edit';
|
||||
if (hash.length > 2) {
|
||||
newURL = `${hash[1]}#${hash[2]}`;
|
||||
}
|
||||
await goto(`${base}/${newURL}`, {
|
||||
replaceState: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,28 +0,0 @@
|
||||
import { base } from '$app/paths';
|
||||
export const get = (): { body: unknown } => {
|
||||
return {
|
||||
body: {
|
||||
short_name: 'Mermaid',
|
||||
name: 'Mermaid Live Editor',
|
||||
icons: [
|
||||
{
|
||||
src: `${base}/icon-192.png`,
|
||||
type: 'image/png',
|
||||
sizes: '192x192'
|
||||
},
|
||||
{
|
||||
src: `${base}/icon-512.png`,
|
||||
type: 'image/png',
|
||||
sizes: '512x512'
|
||||
}
|
||||
],
|
||||
start_url: `${base}/edit/`,
|
||||
background_color: '#6366F1',
|
||||
display: 'standalone',
|
||||
scope: `${base}/edit/`,
|
||||
theme_color: '#6366F1',
|
||||
description: 'FlowChart & Diagrams Editor.',
|
||||
orientation: 'landscape'
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
<script lang="ts">
|
||||
import View from '$lib/components/view.svelte';
|
||||
import { initHandler } from '$lib/util/util';
|
||||
import { onMount } from 'svelte';
|
||||
onMount(initHandler);
|
||||
</script>
|
||||
|
||||
<View />
|
||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 78 KiB |
@@ -1,3 +0,0 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
@@ -1,2 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
self.addEventListener('fetch', (e) => {});
|
||||
@@ -1,30 +0,0 @@
|
||||
import preprocess from 'svelte-preprocess';
|
||||
import adapter from '@sveltejs/adapter-static';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: [
|
||||
preprocess({
|
||||
postcss: true
|
||||
})
|
||||
],
|
||||
|
||||
kit: {
|
||||
adapter: adapter({
|
||||
pages: `docs${process.env['BETA'] ? '/beta' : ''}`
|
||||
}),
|
||||
paths: process.env['DEPLOY']
|
||||
? {
|
||||
base: `${process.env['BETA'] ? '/beta' : ''}/mermaid-live-editor`
|
||||
}
|
||||
: {},
|
||||
ssr: false,
|
||||
// hydrate the <div id="svelte"> element in src/app.html
|
||||
target: '#svelte',
|
||||
trailingSlash: 'ignore'
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,25 +0,0 @@
|
||||
const { tailwindExtractor } = require('tailwindcss/lib/lib/purgeUnusedStyles');
|
||||
|
||||
module.exports = {
|
||||
mode: 'aot',
|
||||
purge: {
|
||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||
options: {
|
||||
defaultExtractor: (content) => [
|
||||
// If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it
|
||||
...tailwindExtractor(content),
|
||||
// Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731)
|
||||
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(
|
||||
([_match, group, ..._rest]) => group
|
||||
)
|
||||
]
|
||||
},
|
||||
safelist: [/^svelte-[\d\w]+$/]
|
||||
},
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
variants: {
|
||||
extend: {}
|
||||
}
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "node",
|
||||
"module": "es2020",
|
||||
"lib": ["es2020", "ESNext"],
|
||||
"target": "es2019",
|
||||
/**
|
||||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
|
||||
to enforce using \`import type\` instead of \`import\` for Types.
|
||||
*/
|
||||
"importsNotUsedAsValues": "error",
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
To have warnings/errors of the Svelte compiler at the correct position,
|
||||
enable source maps by default.
|
||||
*/
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"paths": {
|
||||
"$lib/*": ["src/lib/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.js",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.svelte",
|
||||
"cypress/**/*.ts",
|
||||
"cypress/**/*.js",
|
||||
"static/**/*.js"
|
||||
]
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
|
||||
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-50d0be99.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/start-96e74604.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/vendor-8b597521.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/preload-helper-9f12a5fd.js">
|
||||
<link rel="modulepreload" href="/mermaid-live-editor/_app/chunks/singletons-bb9012b7.js">
|
||||
@@ -67,7 +67,7 @@
|
||||
<link rel="stylesheet" href="/mermaid-live-editor/_app/assets/start-0826e215.css">
|
||||
|
||||
<script type="module">
|
||||
import { start } from "/mermaid-live-editor/_app/start-50d0be99.js";
|
||||
import { start } from "/mermaid-live-editor/_app/start-96e74604.js";
|
||||
start({
|
||||
target: document.querySelector("#svelte"),
|
||||
paths: {"base":"/mermaid-live-editor","assets":"/mermaid-live-editor"},
|
||||