Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65dce606cb |
+1
-3
@@ -11,7 +11,6 @@ module.exports = {
|
|||||||
ignorePatterns: [
|
ignorePatterns: [
|
||||||
'docs/*',
|
'docs/*',
|
||||||
'*.cjs',
|
'*.cjs',
|
||||||
'*.js',
|
|
||||||
'*.md',
|
'*.md',
|
||||||
'snapshots.js',
|
'snapshots.js',
|
||||||
'svelte.config.js',
|
'svelte.config.js',
|
||||||
@@ -28,8 +27,7 @@ module.exports = {
|
|||||||
ecmaVersion: 2019,
|
ecmaVersion: 2019,
|
||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
project: ['./tsconfig.json'],
|
project: ['./tsconfig.json'],
|
||||||
extraFileExtensions: ['.svelte'],
|
extraFileExtensions: ['.svelte']
|
||||||
allowAutomaticSingleRunInference: true
|
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v1
|
||||||
with:
|
with:
|
||||||
config-file: ./.github/codeql/config.yml
|
config-file: ./.github/codeql/config.yml
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
@@ -65,4 +65,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v1
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
cypress-run:
|
cypress-run:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Update after https://github.com/cypress-io/cypress-docker-images/issues/487
|
||||||
|
container: cypress/browsers:node14.16.0-chrome90-ff88
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v2
|
||||||
id: yarn-and-build-cache
|
id: yarn-and-build-cache
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -25,22 +27,17 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-node_modules-build-
|
${{ runner.os }}-node_modules-build-
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
- name: Lint
|
||||||
with:
|
|
||||||
node-version: 16
|
|
||||||
cache: 'yarn'
|
|
||||||
|
|
||||||
- name: Build & Lint
|
|
||||||
run: |
|
run: |
|
||||||
yarn install
|
yarn install
|
||||||
yarn build
|
yarn run lint
|
||||||
yarn lint
|
|
||||||
|
|
||||||
# Install NPM dependencies, cache them correctly
|
# Install NPM dependencies, cache them correctly
|
||||||
# and run all Cypress tests
|
# and run all Cypress tests
|
||||||
- name: Cypress run
|
- name: Cypress run
|
||||||
uses: cypress-io/github-action@v3
|
uses: cypress-io/github-action@v2
|
||||||
with:
|
with:
|
||||||
|
build: yarn build
|
||||||
start: yarn preview
|
start: yarn preview
|
||||||
wait-on: 'http://localhost:3000'
|
wait-on: 'http://localhost:3000'
|
||||||
record: true
|
record: true
|
||||||
|
|||||||
@@ -11,22 +11,37 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/setup-node@v2
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '16'
|
||||||
cache: yarn
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
- name: Build & Deploy
|
- name: Build & Deploy
|
||||||
run: |
|
run: |
|
||||||
|
npm i -g yarn
|
||||||
export DEPLOY=true
|
export DEPLOY=true
|
||||||
|
[ "$GITHUB_EVENT_NAME" == "pull_request" ] && export BETA=true && rm -rf docs/beta/
|
||||||
[ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/
|
[ "$GITHUB_EVENT_NAME" != "pull_request" ] && rm -rf docs/_app/
|
||||||
yarn install
|
yarn install
|
||||||
|
yarn run lint
|
||||||
version=$(yarn version --patch --no-git-tag-version | grep "New version" | cut -d':' -f 2)
|
version=$(yarn version --patch --no-git-tag-version | grep "New version" | cut -d':' -f 2)
|
||||||
yarn build
|
yarn build
|
||||||
yarn run lint
|
cd bin
|
||||||
|
./fix-path
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: docker build . --file Dockerfile --tag $IMAGE_NAME
|
run: docker build . --file Dockerfile --tag $IMAGE_NAME
|
||||||
|
|||||||
@@ -8,13 +8,10 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v5
|
- uses: actions/stale@v4
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
exempt-issue-labels: 'retained'
|
exempt-issue-labels: 'retained'
|
||||||
exempt-pr-labels: 'retained'
|
exempt-pr-labels: 'retained'
|
||||||
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 14 days'
|
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 90 days with no activity. Remove stale label or comment or this will be closed in 14 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'
|
||||||
days-before-stale: 90
|
|
||||||
days-before-close: 14
|
|
||||||
days-before-pr-close: -1
|
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
name: Update Browserslist
|
name: Update Browserslist
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- develop
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- run: npx browserslist@latest --update-db
|
- run: npx browserslist@latest --update-db
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v7
|
||||||
with:
|
with:
|
||||||
author_name: ${{ github.actor }}
|
author_name: ${{ github.actor }}
|
||||||
author_email: ${{ github.actor }}@users.noreply.github.com
|
author_email: ${{ github.actor }}@users.noreply.github.com
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
name: Update Monaco-editor
|
name: Update Monaco-editor
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
pull_request:
|
||||||
|
types: [opened]
|
||||||
branches:
|
branches:
|
||||||
- 'dependabot/npm_and_yarn/develop/monaco-editor-**'
|
- 'dependabot/npm_and_yarn/develop/monaco-editor-**'
|
||||||
|
|
||||||
@@ -9,10 +10,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- run: ./bin/update-monaco
|
- run: ./bin/update-monaco
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: EndBug/add-and-commit@v9
|
uses: EndBug/add-and-commit@v7
|
||||||
with:
|
with:
|
||||||
author_name: ${{ github.actor }}
|
author_name: ${{ github.actor }}
|
||||||
author_email: ${{ github.actor }}@users.noreply.github.com
|
author_email: ${{ github.actor }}@users.noreply.github.com
|
||||||
|
|||||||
@@ -9,6 +9,5 @@ yarn-error.log
|
|||||||
/build
|
/build
|
||||||
/functions
|
/functions
|
||||||
/snapshots.js
|
/snapshots.js
|
||||||
/cypress/downloads
|
|
||||||
/cypress/videos
|
/cypress/videos
|
||||||
/cypress/screenshots
|
/cypress/screenshots
|
||||||
|
|||||||
Vendored
-4
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"cSpell.words": ["pako", "Serde", "serdes"]
|
|
||||||
}
|
|
||||||
+1
-2
@@ -6,12 +6,11 @@
|
|||||||
# Stop : press ctrl + c
|
# Stop : press ctrl + c
|
||||||
# or
|
# or
|
||||||
# docker stop mermaid-live-editor
|
# docker stop mermaid-live-editor
|
||||||
FROM node:18.4.0 as mermaid-live-editor-builder
|
FROM node:17.2.0 as mermaid-live-editor-builder
|
||||||
COPY --chown=node:node . /home
|
COPY --chown=node:node . /home
|
||||||
WORKDIR /home
|
WORKDIR /home
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
FROM nginx:alpine as mermaid-live-editor-runner
|
FROM nginx:alpine as mermaid-live-editor-runner
|
||||||
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
COPY --from=mermaid-live-editor-builder --chown=nginx:nginx /home/docs /usr/share/nginx/html
|
COPY --from=mermaid-live-editor-builder --chown=nginx:nginx /home/docs /usr/share/nginx/html
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
FROM node:18
|
FROM node:17
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
COPY yarn.lock .
|
COPY yarn.lock .
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ docker run --publish 8000:80 ghcr.io/mermaid-js/mermaid-live-editor
|
|||||||
When building, Set the Environment variable MERMAID_RENDERER_URL to the rendering service.
|
When building, Set the Environment variable MERMAID_RENDERER_URL to the rendering service.
|
||||||
Default is `https://mermaid.ink`
|
Default is `https://mermaid.ink`
|
||||||
|
|
||||||
### To configure Kroki Instance URL
|
|
||||||
|
|
||||||
When building, Set the Environment variable MERMAID_KROKI_RENDERER_URL to your Kroki instance.
|
|
||||||
Default is `https://kroki.io`
|
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Live editor only has a single version, which will be maintained.
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| ------- | ------------------ |
|
|
||||||
| latest | :white_check_mark: |
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
To report a vulnerability, please e-mail security@mermaid.live with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { defineConfig } from 'cypress';
|
|
||||||
import fs from 'fs';
|
|
||||||
import { isFileExist, findFiles } from 'cy-verify-downloads';
|
|
||||||
export default defineConfig({
|
|
||||||
projectId: '2ckppp',
|
|
||||||
viewportWidth: 1440,
|
|
||||||
viewportHeight: 768,
|
|
||||||
snapshotFileName: './cypress/snapshots.js',
|
|
||||||
defaultCommandTimeout: 16000,
|
|
||||||
requestTimeout: 16000,
|
|
||||||
e2e: {
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
on('task', {
|
|
||||||
isFileExist,
|
|
||||||
findFiles,
|
|
||||||
deleteFile(path) {
|
|
||||||
fs.rmSync(path);
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
readFileMaybe(filename) {
|
|
||||||
if (fs.existsSync(filename)) {
|
|
||||||
return fs.readFileSync(filename, 'utf8');
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
baseUrl: 'http://localhost:3000',
|
|
||||||
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"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,54 +0,0 @@
|
|||||||
describe('Check actions', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
cy.visit('/edit');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update markdown code', () => {
|
|
||||||
cy.get('#markdown')
|
|
||||||
.invoke('val')
|
|
||||||
.then((oldText) => {
|
|
||||||
cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
|
|
||||||
cy.get('#markdown')
|
|
||||||
.invoke('val')
|
|
||||||
.then((newText) => {
|
|
||||||
expect(oldText).to.not.eq(newText);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should 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', () => {
|
|
||||||
const now = new Date(2022, 0, 1).getTime();
|
|
||||||
cy.clock(now);
|
|
||||||
const downloadsFolder = Cypress.config('downloadsFolder');
|
|
||||||
|
|
||||||
const verifyFileSize = (fileType: string, size: number) => {
|
|
||||||
cy.get(`#download${fileType.toUpperCase()}`).click();
|
|
||||||
const fileName = `mermaid-diagram-2022-01-01-000000.${fileType}`;
|
|
||||||
const filePath = `${downloadsFolder}/${fileName}`;
|
|
||||||
cy.verifyDownload(fileName);
|
|
||||||
cy.readFile(filePath, null, {
|
|
||||||
log: false
|
|
||||||
}).then((buffer) => expect((buffer as ArrayBuffer).byteLength).to.be.gt(size));
|
|
||||||
cy.task('deleteFile', filePath);
|
|
||||||
};
|
|
||||||
|
|
||||||
verifyFileSize('png', 21_000);
|
|
||||||
verifyFileSize('svg', 11_000);
|
|
||||||
|
|
||||||
// Verify downloaded file is different for different diagrams
|
|
||||||
cy.contains('Sample Diagrams').click();
|
|
||||||
cy.contains('ER Diagram').click();
|
|
||||||
|
|
||||||
verifyFileSize('png', 46_000);
|
|
||||||
verifyFileSize('svg', 12_000);
|
|
||||||
|
|
||||||
cy.clock().invoke('restore');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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.github.io/mermaid"]`).should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Test to see if the correct URL loads when changing from one diagram to other', () => {
|
|
||||||
cy.contains('Flow Chart').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/flowchart"]`).should('exist');
|
|
||||||
|
|
||||||
cy.contains('Config').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/flowchart?id=configuration"]`).should('exist');
|
|
||||||
|
|
||||||
cy.contains('Sequence Diagram').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/sequenceDiagram?id=configuration"]`).should('exist');
|
|
||||||
|
|
||||||
cy.contains('Code').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/sequenceDiagram"]`).should('exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Test to check URLs for a case where config URL doesn't exist", () => {
|
|
||||||
cy.contains('State Diagram').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
|
|
||||||
|
|
||||||
cy.contains('Config').click();
|
|
||||||
cy.get(`[data-cy=docs][href$="/#/stateDiagram"]`).should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
import { toBase64 } from 'js-base64';
|
|
||||||
|
|
||||||
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#pako:eNpVkM1qw0AMhF9F6NRC_AI-FBo7zSXQQHLz-iC8cnZJ9oe1TAm2373rmkKrk9B8MwyasAuascRbomjgWisPed6byiQ7iKOhhaJ4m48s4ILn5wz7l2OAwYQYrb-9bvx-haCaTivGIMb6-7JJ1Y__0_MMdXOiKCG2f5XrV5jh0NizyfH_FZM4uz6ansqeio4SVJRa3KHj5MjqXHtaDQrFsGOFZV419zQ-RKHyS0bHqEn4oK2EhDnmMfAOaZRwefoOS0kj_0K1pfwFtx2XbzAdW4g'
|
|
||||||
);
|
|
||||||
cy.contains('History').click();
|
|
||||||
cy.getLocalStorage('codeStore').snapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load sample diagrams when clicked', () => {
|
|
||||||
cy.contains('Sample Diagrams').click();
|
|
||||||
cy.contains('Pie Chart').click();
|
|
||||||
cy.contains('pie title Pets adopted by volunteers');
|
|
||||||
cy.contains('Class Diagram').click();
|
|
||||||
cy.contains('classDiagram');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should 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\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
|
||||||
// true
|
|
||||||
// );
|
|
||||||
// cy.on('window:confirm', () => true);
|
|
||||||
// cy.visit(`/edit#${b64State}`);
|
|
||||||
// cy.contains('Config').click();
|
|
||||||
// cy.contains('forest');
|
|
||||||
// cy.contains('securityLevel').should('not.exist');
|
|
||||||
// cy.get('#view').find('img').should('not.exist');
|
|
||||||
// cy.get('#view').contains('<img');
|
|
||||||
// cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
|
|
||||||
// });
|
|
||||||
|
|
||||||
it('should allow persisting "securityLevel" using confirm dialogue', () => {
|
|
||||||
const b64State = toBase64(
|
|
||||||
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
cy.on('window:confirm', () => false);
|
|
||||||
cy.visit(`/edit#${b64State}`);
|
|
||||||
cy.contains('Config').click();
|
|
||||||
cy.contains('forest');
|
|
||||||
cy.contains('securityLevel');
|
|
||||||
cy.get('#view').find('img').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show troubleshooting steps if loading fails', () => {
|
|
||||||
cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
|
|
||||||
cy.contains('Please Click here to Raise an issue in github.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load uncompressed URL', () => {
|
|
||||||
cy.visit(
|
|
||||||
'/edit/#eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
|
|
||||||
);
|
|
||||||
cy.contains('New Year');
|
|
||||||
cy.visit(
|
|
||||||
'/edit#eyJjb2RlIjoiY2xhc3NEaWFncmFtXG4gICAgQW5pbWFsIDx8LS0gRHVja1xuICAgIEFuaW1hbCA8fC0tIEZpc2hcbiAgICBBbmltYWwgPHwtLSBaZWJyYVxuICAgIEFuaW1hbCA6ICtpbnQgYWdlXG4gICAgQW5pbWFsIDogK1N0cmluZyBnZW5kZXJcbiAgICBBbmltYWw6ICtpc01hbW1hbCgpXG4gICAgQW5pbWFsOiArbWF0ZSgpXG4gICAgY2xhc3MgRHVja3tcbiAgICAgICtTdHJpbmcgYmVha0NvbG9yXG4gICAgICArc3dpbSgpXG4gICAgICArcXVhY2soKVxuICAgIH1cbiAgICBjbGFzcyBGaXNoe1xuICAgICAgLWludCBzaXplSW5GZWV0XG4gICAgICAtY2FuRWF0KClcbiAgICB9XG4gICAgY2xhc3MgWmVicmF7XG4gICAgICArYm9vbCBpc193aWxkXG4gICAgICArcnVuKClcbiAgICB9XG4gICAgICAgICAgICAiLCJtZXJtYWlkIjoie1xuICBcInRoZW1lXCI6IFwiZGFya1wiXG59IiwidXBkYXRlRWRpdG9yIjpmYWxzZSwiYXV0b1N5bmMiOnRydWUsInVwZGF0ZURpYWdyYW0iOmZhbHNlfQ'
|
|
||||||
);
|
|
||||||
cy.contains('Animal');
|
|
||||||
cy.visit(
|
|
||||||
'/edit/#base64:eyJjb2RlIjoiZ3JhcGggVERcbiAgICBBW05ldyBZZWFyXSAtLT58R2V0IG1vbmV5fCBCKEdvIHNob3BwaW5nKVxuICAgIEIgLS0-IEN7TGV0IG1lIHRoaW5rfVxuICAgIEMgLS0-fE9uZXwgRFtMYXB0b3BdXG4gICAgQyAtLT58VHdvfCBFW2lQaG9uZV1cbiAgICBDIC0tPnxUaHJlZXwgRltmYTpmYS1jYXIgQ2FyXSIsIm1lcm1haWQiOiJ7XG4gIFwidGhlbWVcIjogXCJkZWZhdWx0XCJcbn0iLCJ1cGRhdGVFZGl0b3IiOmZhbHNlLCJhdXRvU3luYyI6dHJ1ZSwidXBkYXRlRGlhZ3JhbSI6ZmFsc2V9'
|
|
||||||
);
|
|
||||||
cy.contains('New Year');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('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.reload();
|
|
||||||
cy.contains('Animal');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
describe('Check actions', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.clearLocalStorage();
|
||||||
|
cy.visit('/edit');
|
||||||
|
});
|
||||||
|
it('should update markdown code', () => {
|
||||||
|
cy.get('#markdown')
|
||||||
|
.invoke('val')
|
||||||
|
.then((oldText) => {
|
||||||
|
cy.get('#editor').click('bottom').type('{enter}C --> HistoryTest');
|
||||||
|
cy.get('#markdown')
|
||||||
|
.invoke('val')
|
||||||
|
.then((newText) => {
|
||||||
|
expect(oldText).to.not.eq(newText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load gists from URL', () => {
|
||||||
|
cy.get('#gist').type('https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a');
|
||||||
|
cy.contains('Load Gist').click();
|
||||||
|
cy.contains('Go shopping!!');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,7 +3,6 @@ describe('Auto sync tests', () => {
|
|||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dim diagram when code is edited', () => {
|
it('should dim diagram when code is edited', () => {
|
||||||
cy.contains('Auto sync').click();
|
cy.contains('Auto sync').click();
|
||||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||||
@@ -31,27 +30,4 @@ describe('Auto sync tests', () => {
|
|||||||
cy.get('#view').should('not.have.class', 'outOfSync');
|
cy.get('#view').should('not.have.class', 'outOfSync');
|
||||||
cy.getLocalStorage('codeStore').snapshot();
|
cy.getLocalStorage('codeStore').snapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports commenting code out/in', () => {
|
|
||||||
const cmd = Cypress.platform === 'darwin' ? 'meta' : 'ctrl';
|
|
||||||
|
|
||||||
cy.get('#editor').type(`{uparrow}{${cmd}}/`);
|
|
||||||
cy.get('#view').contains('Car').should('not.exist');
|
|
||||||
|
|
||||||
cy.get('#editor').type(`{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('#editor').type(`{enter}branch test`);
|
|
||||||
cy.get('#editor').contains('branch test').should('exist');
|
|
||||||
cy.get('#errorContainer')
|
|
||||||
.contains(
|
|
||||||
'Error: Trying to checkout branch which is not yet created. (Help try using "branch master")'
|
|
||||||
)
|
|
||||||
.should('exist');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
@@ -46,9 +46,7 @@ describe('Save History', () => {
|
|||||||
cy.get('#historyList').contains('No items in History');
|
cy.get('#historyList').contains('No items in History');
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Fix #639
|
it('should auto save history', () => {
|
||||||
// eslint-disable-next-line mocha/no-skipped-tests
|
|
||||||
xit('should auto save history', () => {
|
|
||||||
cy.get('#editor').type(' C --> HistoryTest');
|
cy.get('#editor').type(' C --> HistoryTest');
|
||||||
cy.tick(70000);
|
cy.tick(70000);
|
||||||
cy.contains('Timeline').click();
|
cy.contains('Timeline').click();
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { toBase64 } from 'js-base64';
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
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\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
cy.on('window:confirm', () => true);
|
||||||
|
cy.visit(`/edit#${b64State}`);
|
||||||
|
cy.contains('Config').click();
|
||||||
|
cy.contains('forest');
|
||||||
|
cy.contains('securityLevel').should('not.exist');
|
||||||
|
cy.get('#view').find('img').should('not.exist');
|
||||||
|
cy.get('#view').contains('<img');
|
||||||
|
cy.get('#view').contains(`src='https://via.placeholder.com/64'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow persisting "securityLevel" using confirm dialogue', () => {
|
||||||
|
const b64State = toBase64(
|
||||||
|
`{"code":"graph TD\\nA[\\"<img src='https://via.placeholder.com/64' width=64/>\\"]","mermaid":"{\\"securityLevel\\": \\"loose\\", \\"theme\\": \\"forest\\"}","updateEditor":true,"autoSync":true,"updateDiagram":true}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
cy.on('window:confirm', () => false);
|
||||||
|
cy.visit(`/edit#${b64State}`);
|
||||||
|
cy.contains('Config').click();
|
||||||
|
cy.contains('forest');
|
||||||
|
cy.contains('securityLevel');
|
||||||
|
cy.get('#view').find('img').should('be.visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show troubleshooting steps if loading fails', () => {
|
||||||
|
cy.visit('/#/edit/eyJjb2RlIjoiZ3JhcGggVERcbiAg');
|
||||||
|
cy.contains('Please Click here to Raise an issue in github.');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -16,7 +16,7 @@ module.exports = {
|
|||||||
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
|
"1": "{\"code\":\"graph TD\\n A[Party] -->|Get money| B(Go shopping!!)\\n \",\"mermaid\":\"{\\n \\\"theme\\\": \\\"forest\\\",\\n \\\"test\\\": \\\"hello world\\\"\\n}\",\"updateEditor\":false,\"autoSync\":true,\"updateDiagram\":true,\"loader\":{\"type\":\"files\",\"config\":{\"codeURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/code.mmd\",\"configURL\":\"https://gist.githubusercontent.com/sidharthv96/6268a23e673a533dcb198f241fd7012a/raw/4eb03887e6a41397e80bdcdbf94017c498f8f1e2/config.json\"}}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"__version": "10.2.0",
|
"__version": "8.5.0",
|
||||||
"Auto sync tests": {
|
"Auto sync tests": {
|
||||||
"should dim diagram when code is edited": {
|
"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}"
|
"1": "{\"code\":\"graph TD\\n A[Christmas] -->|Get money| B(Go shopping)\\n B --> C{Let me think}\\n C -->|One| D[Laptop]\\n C -->|Two| E[iPhone]\\n C -->|Three| F[fa:fa-car Car]\\n C --> Test\",\"mermaid\":\"{\\n \\\"theme\\\": \\\"default\\\"\\n}\",\"updateEditor\":false,\"autoSync\":false,\"updateDiagram\":false}"
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
|
|
||||||
// Import commands.js using ES2015 syntax:
|
// Import commands.js using ES2015 syntax:
|
||||||
import './commands';
|
import './commands';
|
||||||
require('cy-verify-downloads').addCustomCommand();
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"types": ["cypress", "cypress-localstorage-commands", "cy-verify-downloads"]
|
"types": ["cypress", "cypress-localstorage-commands"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts"]
|
"include": ["**/*.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name mermaid;
|
|
||||||
location / {
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
# Fallback to index.html for other paths
|
|
||||||
try_files $uri /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+38
-44
@@ -5,62 +5,58 @@
|
|||||||
"dev": "svelte-kit dev --host 0.0.0.0",
|
"dev": "svelte-kit dev --host 0.0.0.0",
|
||||||
"build": "svelte-kit build",
|
"build": "svelte-kit build",
|
||||||
"preview": "svelte-kit preview",
|
"preview": "svelte-kit preview",
|
||||||
"lint": "prettier --check --cache --plugin-search-dir=. .;eslint --ignore-path .gitignore .",
|
"lint": "prettier --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
||||||
"lint:fix": "prettier --write --cache --plugin-search-dir=. .;eslint --fix --ignore-path .gitignore .",
|
"lint:fix": "prettier --write --plugin-search-dir=. . && eslint --fix --ignore-path .gitignore .",
|
||||||
"format": "prettier --write --cache --plugin-search-dir=. .",
|
"format": "prettier --write --plugin-search-dir=. .",
|
||||||
"pre-commit": "lint-staged",
|
"pre-commit": "lint-staged",
|
||||||
"postinstall": "husky install; cypress cache prune",
|
"postinstall": "husky install",
|
||||||
"test": "cypress run",
|
"test": "cypress run",
|
||||||
"cy": "cypress open"
|
"cy": "cypress open"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/snapshot": "^2.1.7",
|
"@cypress/snapshot": "^2.1.7",
|
||||||
"@sveltejs/adapter-static": "1.0.0-next.34",
|
"@sveltejs/adapter-static": "1.0.0-next.22",
|
||||||
"@sveltejs/kit": "1.0.0-next.354",
|
"@sveltejs/kit": "1.0.0-next.202",
|
||||||
"@types/mermaid": "^8.2.9",
|
"@types/mermaid": "^8.2.7",
|
||||||
"@types/pako": "^1.0.3",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
||||||
"@typescript-eslint/parser": "^4.33.0",
|
"@typescript-eslint/parser": "^4.33.0",
|
||||||
"autoprefixer": "^10.4.7",
|
"autoprefixer": "^10.4.0",
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.4",
|
||||||
"cssnano": "^5.1.12",
|
"cssnano": "^5.0.13",
|
||||||
"cy-verify-downloads": "^0.1.8",
|
"cypress": "9.1.1",
|
||||||
"cypress": "10.2.0",
|
"cypress-localstorage-commands": "^1.6.1",
|
||||||
"cypress-localstorage-commands": "^2.1.0",
|
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.1.0",
|
||||||
"eslint-plugin-cypress": "^2.12.1",
|
"eslint-plugin-cypress": "^2.12.1",
|
||||||
"eslint-plugin-es": "^4.1.0",
|
"eslint-plugin-es": "^4.1.0",
|
||||||
"eslint-plugin-mocha": "^10.0.5",
|
"eslint-plugin-mocha": "^10.0.3",
|
||||||
"eslint-plugin-postcss-modules": "^2.0.0",
|
"eslint-plugin-postcss-modules": "^2.0.0",
|
||||||
"eslint-plugin-svelte3": "^3.4.1",
|
"eslint-plugin-svelte3": "^3.2.1",
|
||||||
"eslint-plugin-tailwindcss": "^3.5.2",
|
"eslint-plugin-tailwindcss": "^1.17.2",
|
||||||
"husky": "^8.0.1",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "13.0.2",
|
"lint-staged": "^12.1.2",
|
||||||
"mocha": "^10.0.0",
|
"mocha": "^9.1.3",
|
||||||
"node-html-parser": "^5.3.3",
|
"node-html-parser": "^5.1.0",
|
||||||
"postcss": "^8.4.14",
|
"postcss": "^8.4.5",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^3.1.0",
|
||||||
"prettier": "~2.7.1",
|
"prettier": "~2.5.1",
|
||||||
"prettier-plugin-svelte": "^2.7.0",
|
"prettier-plugin-svelte": "^2.5.1",
|
||||||
"svelte": "^3.48.0",
|
"svelte": "^3.44.3",
|
||||||
"svelte-preprocess": "4.10.7",
|
"svelte-preprocess": "^4.10.0",
|
||||||
"tailwindcss": "^3.1.4",
|
"tailwindcss": "^3.0.5",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.3.1",
|
||||||
"typescript": "^4.7.3"
|
"typescript": "^4.5.4"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@analytics/google-analytics": "^0.5.3",
|
"@analytics/google-analytics": "^0.5.3",
|
||||||
"@macfja/svelte-persistent-store": "^1.3.0",
|
"@macfja/svelte-persistent-store": "^1.2.0",
|
||||||
"analytics": "^0.8.1",
|
"analytics": "^0.7.19",
|
||||||
"daisyui": "2.17.0",
|
"daisyui": "1.16.5",
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
"mermaid": "9.1.2",
|
"mermaid": "8.13.5",
|
||||||
"moment": "^2.29.3",
|
"moment": "^2.29.1",
|
||||||
"monaco-editor": "^0.33.0",
|
"monaco-editor": "^0.31.1",
|
||||||
"monaco-mermaid": "^1.0.6",
|
|
||||||
"pako": "2.0.4",
|
|
||||||
"random-word-slugs": "^0.1.6"
|
"random-word-slugs": "^0.1.6"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
@@ -70,10 +66,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "16.15.0",
|
"node": "14.16.1",
|
||||||
"yarn": "1.22.10"
|
"yarn": "1.22.10",
|
||||||
},
|
"npm": "7.11.2"
|
||||||
"engines": {
|
|
||||||
"node": ">=16.7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -11,37 +11,37 @@
|
|||||||
<meta
|
<meta
|
||||||
name="description"
|
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." />
|
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.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" />
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" />
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/editor/editor.main.min.css"
|
href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.min.css"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer" />
|
referrerpolicy="no-referrer" />
|
||||||
<script>
|
<script>
|
||||||
var require = {
|
var require = {
|
||||||
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs' }
|
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs' }
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script
|
<script
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/loader.min.js"
|
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/loader.min.js"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer"></script>
|
referrerpolicy="no-referrer"></script>
|
||||||
<script
|
<script
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/editor/editor.main.nls.min.js"
|
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.nls.min.js"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer"></script>
|
referrerpolicy="no-referrer"></script>
|
||||||
<script
|
<script
|
||||||
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.33.0/min/vs/editor/editor.main.js"
|
src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.31.1/min/vs/editor/editor.main.js"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
referrerpolicy="no-referrer"></script>
|
referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
%sveltekit.head%
|
%svelte.head%
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="svelte">%sveltekit.body%</div>
|
<div id="svelte">%svelte.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import type { Handle } from '@sveltejs/kit/types/hooks';
|
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
|
||||||
const response = await resolve(event, {
|
|
||||||
ssr: false
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
@@ -2,26 +2,22 @@
|
|||||||
import { browser } from '$app/env';
|
import { browser } from '$app/env';
|
||||||
|
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import { krokiRendererUrl, rendererUrl } from '$lib/util/env';
|
import { rendererUrl } from '$lib/util/env';
|
||||||
import { pakoSerde } from '$lib/util/serde';
|
import { base64State, codeStore } from '$lib/util/state';
|
||||||
import { stateStore } from '$lib/util/state';
|
|
||||||
import { toBase64 } from 'js-base64';
|
import { toBase64 } from 'js-base64';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
type Exporter = (context: CanvasRenderingContext2D, image: HTMLImageElement) => () => void;
|
||||||
|
|
||||||
const getFileName = (ext: string) =>
|
|
||||||
`mermaid-diagram-${moment().format('YYYY-MM-DD-HHmmss')}.${ext}`;
|
|
||||||
|
|
||||||
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
|
const getBase64SVG = (svg?: HTMLElement, width?: number, height?: number): string => {
|
||||||
svg?.setAttribute('height', `${height}px`);
|
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
|
svg?.setAttribute('width', `${width}px`); // Workaround https://stackoverflow.com/questions/28690643/firefox-error-rendering-an-svg-image-to-html5-canvas-with-drawimage
|
||||||
if (!svg) {
|
if (!svg) {
|
||||||
svg = getSvgEl();
|
svg = document.querySelector('#container svg');
|
||||||
}
|
}
|
||||||
const svgString = svg.outerHTML
|
const svgString = svg.outerHTML
|
||||||
.replaceAll('<br>', '<br/>')
|
.replaceAll('<br>', '<br/>')
|
||||||
.replaceAll(/<img([^>]*)>/g, (m, g: string) => `<img ${g} />`);
|
.replaceAll(/<img([^>]*)>/g, (m, g) => `<img ${g} />`);
|
||||||
return toBase64(svgString);
|
return toBase64(svgString);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,22 +49,6 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSvgEl = () => {
|
|
||||||
const svgEl: HTMLElement = document
|
|
||||||
.querySelector('#container svg')
|
|
||||||
.cloneNode(true) as HTMLElement;
|
|
||||||
const fontAwesomeCdnUrl = Array.from(document.head.getElementsByTagName('link'))
|
|
||||||
.map((l) => l.href)
|
|
||||||
.find((h) => h && h.includes('font-awesome'));
|
|
||||||
if (fontAwesomeCdnUrl == null) {
|
|
||||||
return svgEl;
|
|
||||||
}
|
|
||||||
const styleEl = document.createElement('style');
|
|
||||||
styleEl.innerText = `@import url("${fontAwesomeCdnUrl}");'`;
|
|
||||||
svgEl.prepend(styleEl);
|
|
||||||
return svgEl;
|
|
||||||
};
|
|
||||||
|
|
||||||
const simulateDownload = (download: string, href: string): void => {
|
const simulateDownload = (download: string, href: string): void => {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.download = download;
|
a.download = download;
|
||||||
@@ -81,7 +61,7 @@
|
|||||||
const { canvas } = context;
|
const { canvas } = context;
|
||||||
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
||||||
simulateDownload(
|
simulateDownload(
|
||||||
getFileName('png'),
|
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.png`,
|
||||||
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
|
canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -98,7 +78,7 @@
|
|||||||
canvas.toBlob((blob) => {
|
canvas.toBlob((blob) => {
|
||||||
try {
|
try {
|
||||||
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
|
// @ts-ignore: https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1004/files
|
||||||
void navigator.clipboard.write([
|
navigator.clipboard.write([
|
||||||
/* eslint-disable no-undef */
|
/* eslint-disable no-undef */
|
||||||
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
|
// @ts-ignore: https://github.com/microsoft/TypeScript/issues/43821
|
||||||
new ClipboardItem({
|
new ClipboardItem({
|
||||||
@@ -121,19 +101,22 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onDownloadSVG = () => {
|
const onDownloadSVG = () => {
|
||||||
simulateDownload(getFileName('svg'), `data:image/svg+xml;base64,${getBase64SVG()}`);
|
simulateDownload(
|
||||||
|
`mermaid-diagram-${moment().format('YYYYMMDDHHmmss')}.svg`,
|
||||||
|
`data:image/svg+xml;base64,${getBase64SVG()}`
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCopyMarkdown = () => {
|
const onCopyMarkdown = () => {
|
||||||
(document.getElementById('markdown') as HTMLInputElement).select();
|
document.getElementById('markdown').select();
|
||||||
document.execCommand('Copy');
|
document.execCommand('Copy');
|
||||||
};
|
};
|
||||||
|
|
||||||
let gistURL = '';
|
let gistURL = '';
|
||||||
stateStore.subscribe(({ loader }) => {
|
codeStore.subscribe((state) => {
|
||||||
if (loader?.type === 'gist') {
|
if (state.loader?.type === 'gist') {
|
||||||
// @ts-ignore Gist will have url
|
// @ts-ignore Gist will have url
|
||||||
gistURL = loader.config.url;
|
gistURL = state.loader.config.url;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -146,7 +129,6 @@
|
|||||||
|
|
||||||
let iUrl: string;
|
let iUrl: string;
|
||||||
let svgUrl: string;
|
let svgUrl: string;
|
||||||
let krokiUrl: string;
|
|
||||||
let mdCode: string;
|
let mdCode: string;
|
||||||
let imagemodeselected = 'auto';
|
let imagemodeselected = 'auto';
|
||||||
let userimagesize = 1080;
|
let userimagesize = 1080;
|
||||||
@@ -155,11 +137,15 @@
|
|||||||
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
if (browser && ['mermaid.live', 'netlify'].some((path) => window.location.host.includes(path))) {
|
||||||
isNetlify = true;
|
isNetlify = true;
|
||||||
}
|
}
|
||||||
stateStore.subscribe(({ code, serialized }) => {
|
base64State.subscribe((encodedState: string) => {
|
||||||
iUrl = `${rendererUrl}/img/${serialized}`;
|
const stateCopy = JSON.parse(JSON.stringify($codeStore));
|
||||||
svgUrl = `${rendererUrl}/svg/${serialized}`;
|
if (typeof stateCopy.mermaid === 'string') {
|
||||||
krokiUrl = `${krokiRendererUrl}/mermaid/svg/${pakoSerde.serialize(code)}`;
|
stateCopy.mermaid = JSON.parse(stateCopy.mermaid);
|
||||||
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${serialized})`;
|
}
|
||||||
|
const b64Code = toBase64(JSON.stringify(stateCopy), true);
|
||||||
|
iUrl = `${rendererUrl}/img/${b64Code}`;
|
||||||
|
svgUrl = `${rendererUrl}/svg/${b64Code}`;
|
||||||
|
mdCode = `[](${window.location.protocol}//${window.location.host}${window.location.pathname}#${encodedState})`;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -170,29 +156,18 @@
|
|||||||
><i class="far fa-copy mr-2" /> Copy Image to clipboard
|
><i class="far fa-copy mr-2" /> Copy Image to clipboard
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<button id="downloadPNG" class="action-btn flex-auto" on:click={onDownloadPNG}>
|
<button class="action-btn flex-auto" on:click={onDownloadPNG}>
|
||||||
<i class="fas fa-download mr-2" /> PNG
|
<i class="fas fa-download mr-2" /> PNG
|
||||||
</button>
|
</button>
|
||||||
<button id="downloadSVG" class="action-btn flex-auto" on:click={onDownloadSVG}>
|
<button class="action-btn flex-auto" on:click={onDownloadSVG}>
|
||||||
<i class="fas fa-download mr-2" /> SVG
|
<i class="fas fa-download mr-2" /> SVG
|
||||||
</button>
|
</button>
|
||||||
<a target="_blank" href={iUrl}>
|
<button class="action-btn flex-auto">
|
||||||
<button class="action-btn flex-auto">
|
<a target="_blank" href={iUrl}><i class="fas fa-external-link-alt mr-2" /> PNG</a>
|
||||||
<i class="fas fa-external-link-alt mr-2" /> PNG
|
</button>
|
||||||
</button>
|
<button class="action-btn flex-auto">
|
||||||
</a>
|
<a target="_blank" href={svgUrl}><i class="fas fa-external-link-alt mr-2" /> SVG</a>
|
||||||
|
</button>
|
||||||
<a target="_blank" href={svgUrl}>
|
|
||||||
<button class="action-btn flex-auto">
|
|
||||||
<i class="fas fa-external-link-alt mr-2" /> SVG
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a target="_blank" href={krokiUrl}>
|
|
||||||
<button class="action-btn flex-auto">
|
|
||||||
<i class="fas fa-external-link-alt mr-2" /> Kroki
|
|
||||||
</button>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="flex gap-2 items-center">
|
<div class="flex gap-2 items-center">
|
||||||
PNG size
|
PNG size
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<ul class="tabs" transition:fade>
|
<ul class="tabs" transition:fade>
|
||||||
{#each tabs as tab}
|
{#each tabs as tab}
|
||||||
<div
|
<div
|
||||||
class="tab tab-lifted {activeTabID === tab.id ? 'tab-active' : 'text-primary-content'}"
|
class="tab tab-lifted text-primary-content {activeTabID === tab.id ? 'tab-active' : ''}"
|
||||||
on:click|stopPropagation={() => toggleTabs(tab)}>
|
on:click|stopPropagation={() => toggleTabs(tab)}>
|
||||||
<i class="mr-1 {tab.icon}" />
|
<i class="mr-1 {tab.icon}" />
|
||||||
{tab.title}
|
{tab.title}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { EditorEvents } from '$lib/types';
|
import type { EditorEvents } from '$lib/types';
|
||||||
import { stateStore } from '$lib/util/state';
|
import { codeStore } from '$lib/util/state';
|
||||||
import { themeStore } from '$lib/util/theme';
|
import { themeStore } from '$lib/util/theme';
|
||||||
import type monaco from 'monaco-editor';
|
import type monaco from 'monaco-editor';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
import initEditor from 'monaco-mermaid';
|
import { initEditor } from './util';
|
||||||
|
|
||||||
let divEl: HTMLDivElement = null;
|
let divEl: HTMLDivElement = null;
|
||||||
let editor: monaco.editor.IStandaloneCodeEditor;
|
let editor: monaco.editor.IStandaloneCodeEditor;
|
||||||
@@ -21,16 +21,17 @@
|
|||||||
theme: 'mermaid',
|
theme: 'mermaid',
|
||||||
overviewRulerLanes: 0
|
overviewRulerLanes: 0
|
||||||
};
|
};
|
||||||
|
export let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||||
let oldText = text;
|
let oldText = text;
|
||||||
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
$: editor && Monaco?.editor.setModelLanguage(editor.getModel(), language);
|
||||||
$: {
|
$: {
|
||||||
if (text !== oldText) {
|
if (text !== oldText) {
|
||||||
if ($stateStore.updateEditor) {
|
if ($codeStore.updateEditor) {
|
||||||
editor?.setValue(text);
|
editor?.setValue(text);
|
||||||
}
|
}
|
||||||
oldText = text;
|
oldText = text;
|
||||||
}
|
}
|
||||||
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', $stateStore.errorMarkers);
|
editor && Monaco?.editor.setModelMarkers(editor.getModel(), 'test', errorMarkers);
|
||||||
}
|
}
|
||||||
|
|
||||||
themeStore.subscribe(({ isDark }) => {
|
themeStore.subscribe(({ isDark }) => {
|
||||||
@@ -0,0 +1,552 @@
|
|||||||
|
/* 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' });
|
||||||
|
|
||||||
|
const requirementDiagrams = [
|
||||||
|
'requirement',
|
||||||
|
'functionalRequirement',
|
||||||
|
'interfaceRequirement',
|
||||||
|
'performanceRequirement',
|
||||||
|
'physicalRequirement',
|
||||||
|
'designConstraint'
|
||||||
|
];
|
||||||
|
|
||||||
|
const keywords: {
|
||||||
|
[key: string]: {
|
||||||
|
typeKeywords: string[];
|
||||||
|
blockKeywords: string[];
|
||||||
|
keywords: string[];
|
||||||
|
};
|
||||||
|
} = {
|
||||||
|
flowchart: {
|
||||||
|
typeKeywords: ['flowchart', 'flowchart-v2', 'graph'],
|
||||||
|
blockKeywords: ['subgraph', 'end'],
|
||||||
|
keywords: [
|
||||||
|
'TB',
|
||||||
|
'TD',
|
||||||
|
'BT',
|
||||||
|
'RL',
|
||||||
|
'LR',
|
||||||
|
'click',
|
||||||
|
'call',
|
||||||
|
'href',
|
||||||
|
'_self',
|
||||||
|
'_blank',
|
||||||
|
'_parent',
|
||||||
|
'_top',
|
||||||
|
'linkStyle',
|
||||||
|
'style',
|
||||||
|
'classDef',
|
||||||
|
'class',
|
||||||
|
'direction',
|
||||||
|
'interpolate'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sequenceDiagram: {
|
||||||
|
typeKeywords: ['sequenceDiagram'],
|
||||||
|
blockKeywords: ['alt', 'par', 'and', 'loop', 'else', 'end', 'rect', 'opt', 'alt', 'rect'],
|
||||||
|
keywords: [
|
||||||
|
'participant',
|
||||||
|
'as',
|
||||||
|
'Note',
|
||||||
|
'note',
|
||||||
|
'right of',
|
||||||
|
'left of',
|
||||||
|
'over',
|
||||||
|
'activate',
|
||||||
|
'deactivate',
|
||||||
|
'autonumber',
|
||||||
|
'title',
|
||||||
|
'actor'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
classDiagram: {
|
||||||
|
typeKeywords: ['classDiagram', 'classDiagram-v2'],
|
||||||
|
blockKeywords: ['class'],
|
||||||
|
keywords: [
|
||||||
|
'link',
|
||||||
|
'click',
|
||||||
|
'callback',
|
||||||
|
'call',
|
||||||
|
'href',
|
||||||
|
'cssClass',
|
||||||
|
'direction',
|
||||||
|
'TB',
|
||||||
|
'BT',
|
||||||
|
'RL',
|
||||||
|
'LR'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
stateDiagram: {
|
||||||
|
typeKeywords: ['stateDiagram', 'stateDiagram-v2'],
|
||||||
|
blockKeywords: ['state', 'note', 'end'],
|
||||||
|
keywords: ['state', 'as', 'hide empty description', 'direction', 'TB', 'BT', 'RL', 'LR']
|
||||||
|
},
|
||||||
|
erDiagram: {
|
||||||
|
typeKeywords: ['erDiagram'],
|
||||||
|
blockKeywords: [],
|
||||||
|
keywords: []
|
||||||
|
},
|
||||||
|
journey: {
|
||||||
|
typeKeywords: ['journey'],
|
||||||
|
blockKeywords: ['section'],
|
||||||
|
keywords: ['title']
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
typeKeywords: ['info'],
|
||||||
|
blockKeywords: [],
|
||||||
|
keywords: ['showInfo']
|
||||||
|
},
|
||||||
|
gantt: {
|
||||||
|
typeKeywords: ['gantt'],
|
||||||
|
blockKeywords: [],
|
||||||
|
keywords: [
|
||||||
|
'title',
|
||||||
|
'dateFormat',
|
||||||
|
'axisFormat',
|
||||||
|
'todayMarker',
|
||||||
|
'section',
|
||||||
|
'excludes',
|
||||||
|
'inclusiveEndDates'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
requirementDiagram: {
|
||||||
|
typeKeywords: ['requirement', 'requirementDiagram'],
|
||||||
|
blockKeywords: requirementDiagrams.concat('element'),
|
||||||
|
keywords: []
|
||||||
|
},
|
||||||
|
gitGraph: {
|
||||||
|
typeKeywords: ['gitGraph'],
|
||||||
|
blockKeywords: [],
|
||||||
|
keywords: ['commit', 'branch', 'merge', 'reset', 'checkout', 'LR', 'BT']
|
||||||
|
},
|
||||||
|
pie: {
|
||||||
|
typeKeywords: ['pie'],
|
||||||
|
blockKeywords: [],
|
||||||
|
keywords: ['title', 'showData']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register a tokens provider for the mermaid language
|
||||||
|
monacoEditor.languages.setMonarchTokensProvider('mermaid', {
|
||||||
|
...Object.entries(keywords)
|
||||||
|
.map((entry) =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(entry[1]).map((deepEntry) => [
|
||||||
|
entry[0] + deepEntry[0][0].toUpperCase() + deepEntry[0].slice(1),
|
||||||
|
deepEntry[1]
|
||||||
|
])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.reduce((overallKeywords, nextKeyword) => ({ ...overallKeywords, ...nextKeyword }), {}),
|
||||||
|
tokenizer: {
|
||||||
|
root: [
|
||||||
|
[/%%(?=.*%%$)/, { token: 'string', nextEmbedded: 'json' }],
|
||||||
|
[/%%$/, { token: 'string', nextEmbedded: '@pop' }],
|
||||||
|
[/^\s*gitGraph/m, 'typeKeyword', 'gitGraph'],
|
||||||
|
[/^\s*info/m, 'typeKeyword', 'info'],
|
||||||
|
[/^\s*pie/m, 'typeKeyword', 'pie'],
|
||||||
|
[/^\s*(flowchart|flowchart-v2|graph)/m, 'typeKeyword', 'flowchart'],
|
||||||
|
[/^\s*sequenceDiagram/, 'typeKeyword', 'sequenceDiagram'],
|
||||||
|
[/^\s*classDiagram(-v2)?/, 'typeKeyword', 'classDiagram'],
|
||||||
|
[/^\s*journey/, 'typeKeyword', 'journey'],
|
||||||
|
[/^\s*gantt/, 'typeKeyword', 'gantt'],
|
||||||
|
[/^\s*stateDiagram(-v2)?/, 'typeKeyword', 'stateDiagram'],
|
||||||
|
[/^\s*erDiagram/, 'typeKeyword', 'erDiagram'],
|
||||||
|
[/^\s*requirement(Diagram)?/, 'typeKeyword', 'requirementDiagram'],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
|
||||||
|
],
|
||||||
|
gitGraph: [
|
||||||
|
[/option(?=s)/, { token: 'typeKeyword', next: 'optionsGitGraph' }],
|
||||||
|
[/(^\s*branch|reset|merge|checkout)(.*$)/, ['keyword', 'variable']],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@gitGraphBlockKeywords': 'typeKeyword',
|
||||||
|
'@gitGraphKeywords': 'keyword'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/\^/, 'delimiter.bracket']
|
||||||
|
],
|
||||||
|
optionsGitGraph: [
|
||||||
|
[/s$/, { token: 'typeKeyword', nextEmbedded: 'json', matchOnlyAtLineStart: false }],
|
||||||
|
['end', { token: 'typeKeyword', next: '@pop', nextEmbedded: '@pop' }]
|
||||||
|
],
|
||||||
|
info: [
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@infoBlockKeywords': 'typeKeyword',
|
||||||
|
'@infoKeywords': 'keyword'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
pie: [
|
||||||
|
[/(title)(.*$)/, ['keyword', 'string']],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@pieBlockKeywords': 'typeKeyword',
|
||||||
|
'@pieKeywords': 'keyword'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/\s*\d+/, 'number'],
|
||||||
|
[/:/, 'delimiter.bracket'],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
|
||||||
|
],
|
||||||
|
flowchart: [
|
||||||
|
[/[ox]?(--+|==+)[ox]/, 'transition'],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@flowchartBlockKeywords': 'typeKeyword',
|
||||||
|
'@flowchartKeywords': 'keyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/\|+.+?\|+/, 'string'],
|
||||||
|
[/\[+(\\.+?[\\/]|\/.+?[/\\])\]+/, 'string'],
|
||||||
|
[/[[>]+[^\]|[]+?\]+/, 'string'],
|
||||||
|
[/{+.+?}+/, 'string'],
|
||||||
|
[/\(+.+?\)+/, 'string'],
|
||||||
|
[/-\.+->?/, 'transition'],
|
||||||
|
[/(-[-.])([^->]+?)(-{3,}|-{2,}>|\.-+>)/, ['transition', 'string', 'transition']],
|
||||||
|
[/(==+)([^=]+?)(={3,}|={2,}>)/, ['transition', 'string', 'transition']],
|
||||||
|
[/<?(--+|==+)>|===+|---+/, 'transition'],
|
||||||
|
[/:::/, 'transition'],
|
||||||
|
[/[;&]/, 'delimiter.bracket'],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
|
||||||
|
],
|
||||||
|
sequenceDiagram: [
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@sequenceDiagramBlockKeywords': 'typeKeyword',
|
||||||
|
'@sequenceDiagramKeywords': 'keyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/(--?>?>|--?[)x])[+-]?/, 'transition'],
|
||||||
|
[/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
|
||||||
|
],
|
||||||
|
classDiagram: [
|
||||||
|
[/(?!class\s)([a-zA-Z]+)(\s+[a-zA-Z]+)/, ['type', 'variable']],
|
||||||
|
[/(\*|<\|?|o)?(--|\.\.)(\*|\|?>|o)?/, 'transition'],
|
||||||
|
[/^\s*class\s(?!.*\{)/, 'keyword'],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@classDiagramBlockKeywords': 'typeKeyword',
|
||||||
|
'@classDiagramKeywords': 'keyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/(<<)(.+?)(>>)/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/:::/, 'transition'],
|
||||||
|
[/:|\+|-|#|~|\*\s*$|\$\s*$|\(|\)|{|}/, 'delimiter.bracket']
|
||||||
|
],
|
||||||
|
journey: [
|
||||||
|
[/(title)(.*)/, ['keyword', 'string']],
|
||||||
|
[/(section)(.*)/, ['typeKeyword', 'string']],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@journeyBlockKeywords': 'typeKeyword',
|
||||||
|
'@journeyKeywords': 'keyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/(^\s*.+?)(:)(.*?)(:)(.*?)([,$])/,
|
||||||
|
[
|
||||||
|
'string',
|
||||||
|
'delimiter.bracket',
|
||||||
|
'number',
|
||||||
|
'delimiter.bracket',
|
||||||
|
'variable',
|
||||||
|
'delimiter.bracket'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[/,/, 'delimiter.bracket'],
|
||||||
|
[/(^\s*.+?)(:)([^:]*?)$/, ['string', 'delimiter.bracket', 'variable']],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment']
|
||||||
|
],
|
||||||
|
gantt: [
|
||||||
|
[/(title)(.*)/, ['keyword', 'string']],
|
||||||
|
[/(section)(.*)/, ['typeKeyword', 'string']],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@ganttBlockKeywords': 'typeKeyword',
|
||||||
|
'@ganttKeywords': 'keyword'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/(^\s*.*?)(:)/, ['string', 'delimiter.bracket']],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/:/, 'delimiter.bracket']
|
||||||
|
],
|
||||||
|
stateDiagram: [
|
||||||
|
[/note[^:]*$/, { token: 'typeKeyword', next: 'stateDiagramNote' }],
|
||||||
|
['hide empty description', 'keyword'],
|
||||||
|
[/^\s*state\s(?!.*\{)/, 'keyword'],
|
||||||
|
[/(<<)(fork|join|choice)(>>)/, 'annotation'],
|
||||||
|
[/(\[\[)(fork|join|choice)(]])/, ['delimiter.bracket', 'annotation', 'delimiter.bracket']],
|
||||||
|
[
|
||||||
|
/[a-zA-Z][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@stateDiagramBlockKeywords': 'typeKeyword',
|
||||||
|
'@stateDiagramKeywords': 'keyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/(:)([^:\n]*?$)/, ['delimiter.bracket', 'string']],
|
||||||
|
[/{|}/, 'delimiter.bracket'],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/-->/, 'transition'],
|
||||||
|
[/\[.*?]/, 'string']
|
||||||
|
],
|
||||||
|
stateDiagramNote: [
|
||||||
|
[/^\s*end note$/, { token: 'typeKeyword', next: '@pop' }],
|
||||||
|
[/.*/, 'string']
|
||||||
|
],
|
||||||
|
erDiagram: [
|
||||||
|
[/[}|][o|](--|\.\.)[o|][{|]/, 'transition'],
|
||||||
|
[/".*?"/, 'string'],
|
||||||
|
[/(:)(.*?$)/, ['delimiter.bracket', 'string']],
|
||||||
|
[/:|{|}/, 'delimiter.bracket'],
|
||||||
|
[/([a-zA-Z]+)(\s+[a-zA-Z]+)/, ['type', 'variable']],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/[a-zA-Z_-][\w$]*/, 'variable']
|
||||||
|
],
|
||||||
|
requirementDiagram: [
|
||||||
|
[/->|<-|-/, 'transition'],
|
||||||
|
[/(\d+\.)*\d+/, 'number'],
|
||||||
|
[
|
||||||
|
/[a-zA-Z_-][\w$]*/,
|
||||||
|
{
|
||||||
|
cases: {
|
||||||
|
'@requirementDiagramBlockKeywords': 'typeKeyword',
|
||||||
|
'@default': 'variable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[/:|{|}|\//, 'delimiter.bracket'],
|
||||||
|
[/%%[^$]([^%]*(?!%%$)%?)*$/, 'comment'],
|
||||||
|
[/".*?"/, 'string']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
monacoEditor.editor.defineTheme('mermaid-dark', {
|
||||||
|
base: 'vs-dark',
|
||||||
|
inherit: true,
|
||||||
|
rules: [
|
||||||
|
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
|
||||||
|
{ token: 'transition', foreground: '008800', fontStyle: 'bold' }
|
||||||
|
],
|
||||||
|
colors: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
monacoEditor.editor.defineTheme('mermaid', {
|
||||||
|
base: 'vs',
|
||||||
|
inherit: true,
|
||||||
|
rules: [
|
||||||
|
{ token: 'typeKeyword', foreground: '9650c8', fontStyle: 'bold' },
|
||||||
|
{ token: 'keyword', foreground: '649696' },
|
||||||
|
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
|
||||||
|
{ token: 'string', foreground: 'AA8500' },
|
||||||
|
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
|
||||||
|
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' },
|
||||||
|
{ token: 'annotation', foreground: '4b4b96' },
|
||||||
|
{ token: 'number', foreground: '4b4b96' },
|
||||||
|
{ token: 'comment', foreground: '888c89' },
|
||||||
|
{ token: 'variable', foreground: 'A22889' },
|
||||||
|
{ token: 'type', foreground: '2BDEA8' }
|
||||||
|
],
|
||||||
|
colors: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register a completion item provider for the mermaid language
|
||||||
|
monacoEditor.languages.registerCompletionItemProvider('mermaid', {
|
||||||
|
provideCompletionItems: () => {
|
||||||
|
const suggestions = [
|
||||||
|
{
|
||||||
|
label: 'loop',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['loop ${1:Loop text}', '\t$0', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Sequence Diagram Loops'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'alt',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['alt ${1:Describing text}', '\t$0', 'else', '\t', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Alternative Path'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'opt',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['opt ${1:Describing text}', '\t$0', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Optional Path'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'par',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: [
|
||||||
|
'par ${1:[Action 1]}',
|
||||||
|
'\t$0',
|
||||||
|
'and ${2:[Action 2]}',
|
||||||
|
'\t',
|
||||||
|
'and ${3:[Action 3]}',
|
||||||
|
'\t',
|
||||||
|
'end'
|
||||||
|
].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Parallel Actions'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'rect',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['rect ${1:rgb(0, 255, 0)}', '\t$0', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Background Color'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'subgraph',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['subgraph ${1:title}', '\t$0', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Subgraph'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'class',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['class ${1:className} {', '\t$0', '}'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Class'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'state',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['state ${1:stateName} {', '\t$0', '}'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'State'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'note',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['note ${1:right of State1}', '\t$0', 'end note'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'State'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'section',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['section ${1:Go to work}', '\t$0'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'User-journey Section'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'element',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['element ${1:test_entity} {', '\t$0', '}'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Requirement Diagram Element'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'options',
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: ['options', '{', ' $0', '}', 'end'].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: 'Git Graph Options'
|
||||||
|
},
|
||||||
|
...requirementDiagrams.map((requirementDiagramType) => ({
|
||||||
|
label: requirementDiagramType,
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Snippet,
|
||||||
|
insertText: [
|
||||||
|
requirementDiagramType + ' ${1:test_req} {',
|
||||||
|
'\tid: 1',
|
||||||
|
'\ttext: the test text.',
|
||||||
|
'\trisk: high',
|
||||||
|
'\tverifyMethod: test',
|
||||||
|
'}'
|
||||||
|
].join('\n'),
|
||||||
|
insertTextRules: monacoEditor.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||||
|
documentation: requirementDiagramType
|
||||||
|
.split(/(?=[A-Z])/)
|
||||||
|
.map((part) => part[0].toUpperCase() + part.slice(1))
|
||||||
|
.join(' ')
|
||||||
|
})),
|
||||||
|
...[
|
||||||
|
...new Set(
|
||||||
|
Object.values(keywords)
|
||||||
|
.map((diagramKeywords) =>
|
||||||
|
Object.entries(diagramKeywords)
|
||||||
|
.filter((keywordType) => keywordType[0] !== 'annotations')
|
||||||
|
.map((entry) => entry[1])
|
||||||
|
)
|
||||||
|
.flat(2)
|
||||||
|
)
|
||||||
|
].map((keyword) => ({
|
||||||
|
label: keyword,
|
||||||
|
kind: monacoEditor.languages.CompletionItemKind.Keyword,
|
||||||
|
insertText: keyword
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
return { suggestions: suggestions };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
monacoEditor.languages.setLanguageConfiguration('mermaid', {
|
||||||
|
autoClosingPairs: [
|
||||||
|
{
|
||||||
|
open: '(',
|
||||||
|
close: ')'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
open: '{',
|
||||||
|
close: '}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
open: '[',
|
||||||
|
close: ']'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
brackets: [
|
||||||
|
['(', ')'],
|
||||||
|
['{', '}'],
|
||||||
|
['[', ']']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import { inputStateStore, getStateString } from '$lib/util/state';
|
import { codeStore, getStateString } from '$lib/util/state';
|
||||||
import {
|
import {
|
||||||
addHistoryEntry,
|
addHistoryEntry,
|
||||||
historyModeStore,
|
historyModeStore,
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
const previousState: string = getPreviousState(auto);
|
const previousState: string = getPreviousState(auto);
|
||||||
if (previousState !== currentState) {
|
if (previousState !== currentState) {
|
||||||
addHistoryEntry({
|
addHistoryEntry({
|
||||||
state: $inputStateStore,
|
state: $codeStore,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
type: auto ? 'auto' : 'manual'
|
type: auto ? 'auto' : 'manual'
|
||||||
});
|
});
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const restoreHistory = (state: State): void => {
|
const restoreHistory = (state: State): void => {
|
||||||
inputStateStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
codeStore.set({ ...state, updateEditor: true, updateDiagram: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const relativeTime = (time: number) => {
|
const relativeTime = (time: number) => {
|
||||||
|
|||||||
@@ -69,6 +69,24 @@
|
|||||||
ORDER ||--|{ ORDER-ITEM : includes
|
ORDER ||--|{ ORDER-ITEM : includes
|
||||||
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
PRODUCT-CATEGORY ||--|{ PRODUCT : contains
|
||||||
PRODUCT ||--o{ ORDER-ITEM : "ordered in"
|
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
|
'User Journey': ` journey
|
||||||
title My working day
|
title My working day
|
||||||
@@ -79,26 +97,11 @@
|
|||||||
section Go home
|
section Go home
|
||||||
Go downstairs: 5: Me
|
Go downstairs: 5: Me
|
||||||
Sit down: 3: Me
|
Sit down: 3: Me
|
||||||
`,
|
`
|
||||||
'Git Graph': ` gitGraph
|
|
||||||
commit
|
|
||||||
commit
|
|
||||||
branch develop
|
|
||||||
checkout develop
|
|
||||||
commit
|
|
||||||
commit
|
|
||||||
checkout main
|
|
||||||
merge develop
|
|
||||||
commit
|
|
||||||
commit
|
|
||||||
`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadSampleDiagram = (diagramType: string): void => {
|
const loadSampleDiagram = (diagramType: string): void => {
|
||||||
updateCode(samples[diagramType], {
|
updateCode(samples[diagramType], true, true);
|
||||||
updateDiagram: true,
|
|
||||||
updateEditor: true
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { inputStateStore, stateStore } from '$lib/util/state';
|
import { errorStore } from '$lib/util/error';
|
||||||
|
|
||||||
|
import { codeStore } from '$lib/util/state';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import mermaid from 'mermaid';
|
import mermaid from 'mermaid';
|
||||||
|
|
||||||
@@ -11,16 +13,11 @@
|
|||||||
let outOfSync = false;
|
let outOfSync = false;
|
||||||
let manualUpdate = true;
|
let manualUpdate = true;
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
stateStore.subscribe((state) => {
|
codeStore.subscribe((state) => {
|
||||||
if (state.error !== undefined) {
|
|
||||||
error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
error = false;
|
|
||||||
try {
|
try {
|
||||||
if (container && state && (state.updateDiagram || state.autoSync)) {
|
if (container && state && (state.updateDiagram || state.autoSync)) {
|
||||||
if (!state.autoSync) {
|
if (!state.autoSync) {
|
||||||
$inputStateStore.updateDiagram = false;
|
$codeStore.updateDiagram = false;
|
||||||
}
|
}
|
||||||
outOfSync = false;
|
outOfSync = false;
|
||||||
manualUpdate = true;
|
manualUpdate = true;
|
||||||
@@ -34,10 +31,7 @@
|
|||||||
delete container.dataset.processed;
|
delete container.dataset.processed;
|
||||||
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid)));
|
||||||
mermaid.render('graph-div', code, (svgCode) => {
|
mermaid.render('graph-div', code, (svgCode) => {
|
||||||
if (svgCode.length > 0) {
|
container.innerHTML = svgCode;
|
||||||
console.log(svgCode);
|
|
||||||
container.innerHTML = svgCode;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
view.parentElement.scrollTop = scroll;
|
view.parentElement.scrollTop = scroll;
|
||||||
error = false;
|
error = false;
|
||||||
@@ -51,19 +45,24 @@
|
|||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
errorStore.subscribe((err) => {
|
||||||
|
if (typeof err === 'undefined') {
|
||||||
|
error = false;
|
||||||
|
} else {
|
||||||
|
error = true;
|
||||||
|
console.log('Error: ', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if error && $stateStore.error instanceof Error}
|
|
||||||
<div class="p-2 text-red-600" id="errorContainer">{$stateStore.error}</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div id="view" bind:this={view} class="p-2" class:error class:outOfSync>
|
<div id="view" bind:this={view} class="p-2" class:error class:outOfSync>
|
||||||
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
<div id="container" bind:this={container} class="flex-1 overflow-auto" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#view {
|
#view {
|
||||||
|
border: 1px solor darkred;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.error,
|
.error,
|
||||||
|
|||||||
Vendored
+1
-24
@@ -6,16 +6,6 @@ export interface Locals {
|
|||||||
userid: string;
|
userid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkerData {
|
|
||||||
severity: number;
|
|
||||||
message: string;
|
|
||||||
source?: string;
|
|
||||||
startLineNumber: number;
|
|
||||||
startColumn: number;
|
|
||||||
endLineNumber: number;
|
|
||||||
endColumn: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EditorUpdateEvent {
|
export interface EditorUpdateEvent {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
@@ -42,12 +32,6 @@ export interface State {
|
|||||||
loader?: LoaderConfig;
|
loader?: LoaderConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ValidatedState extends State {
|
|
||||||
error: any;
|
|
||||||
errorMarkers: MarkerData[];
|
|
||||||
serialized: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GistLoaderConfig {
|
export interface GistLoaderConfig {
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
@@ -73,11 +57,4 @@ export interface HistoryEntry {
|
|||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocConfig {
|
type Loader = (url: string) => Promise<State>;
|
||||||
[key: string]: {
|
|
||||||
code: string;
|
|
||||||
config?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Loader = (url: string) => Promise<State>;
|
|
||||||
|
|||||||
+1
-4
@@ -1,4 +1 @@
|
|||||||
export const rendererUrl: string =
|
export const rendererUrl = import.meta.env.MERMAID_RENDERER_URL ?? 'https://mermaid.ink';
|
||||||
(import.meta.env.MERMAID_RENDERER_URL as string) ?? 'https://mermaid.ink';
|
|
||||||
export const krokiRendererUrl: string =
|
|
||||||
(import.meta.env.MERMAID_KROKI_RENDERER_URL as string) ?? 'https://kroki.io';
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export const errorStore = writable(undefined);
|
||||||
@@ -54,4 +54,5 @@ export const loadDataFromUrl = async (): Promise<void> => {
|
|||||||
updateDiagram: true,
|
updateDiagram: true,
|
||||||
updateEditor: true
|
updateEditor: true
|
||||||
});
|
});
|
||||||
|
// window.location.search = '';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
import { deflate, inflate } from 'pako';
|
|
||||||
import { toUint8Array, fromUint8Array, toBase64, fromBase64 } from 'js-base64';
|
|
||||||
import type { State } from '$lib/types';
|
|
||||||
|
|
||||||
interface Serde {
|
|
||||||
serialize: (state: string) => string;
|
|
||||||
deserialize: (state: string) => string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const base64Serde: Serde = {
|
|
||||||
serialize: (state: string): string => {
|
|
||||||
return toBase64(state, true);
|
|
||||||
},
|
|
||||||
deserialize: (state: string): string => {
|
|
||||||
return fromBase64(state);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pakoSerde: Serde = {
|
|
||||||
serialize: (state: string): string => {
|
|
||||||
const data = new TextEncoder().encode(state);
|
|
||||||
const compressed = deflate(data, { level: 9 });
|
|
||||||
return fromUint8Array(compressed, true);
|
|
||||||
},
|
|
||||||
deserialize: (state: string): string => {
|
|
||||||
const data = toUint8Array(state);
|
|
||||||
return inflate(data, { to: 'string' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const serdes: { [key: string]: Serde } = {
|
|
||||||
base64: base64Serde,
|
|
||||||
pako: pakoSerde
|
|
||||||
};
|
|
||||||
|
|
||||||
type SerdeType = keyof typeof serdes;
|
|
||||||
|
|
||||||
export const serializeState = (state: State): string => {
|
|
||||||
const json = JSON.stringify(state);
|
|
||||||
const defaultSerde: SerdeType = 'pako';
|
|
||||||
const serialized = serdes[defaultSerde].serialize(json);
|
|
||||||
return `${defaultSerde}:${serialized}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deserializeState = (state: string): State => {
|
|
||||||
let type: SerdeType, serialized: string;
|
|
||||||
if (state.includes(':')) {
|
|
||||||
let tempType: string;
|
|
||||||
[tempType, serialized] = state.split(':');
|
|
||||||
if (tempType in serdes) {
|
|
||||||
type = tempType;
|
|
||||||
} else {
|
|
||||||
throw new Error(`Unknown serde type: ${tempType}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = 'base64';
|
|
||||||
serialized = state;
|
|
||||||
}
|
|
||||||
const json = serdes[type].deserialize(serialized);
|
|
||||||
return JSON.parse(json) as State;
|
|
||||||
};
|
|
||||||
+22
-59
@@ -1,11 +1,9 @@
|
|||||||
import { writable, get, derived } from 'svelte/store';
|
import { writable, get, derived } from 'svelte/store';
|
||||||
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
|
||||||
import { saveStatistics } from './stats';
|
|
||||||
import { serializeState, deserializeState } from './serde';
|
|
||||||
import mermaid from 'mermaid';
|
|
||||||
|
|
||||||
import type { Readable } from 'svelte/store';
|
import type { Readable } from 'svelte/store';
|
||||||
import type { MarkerData, State, ValidatedState } from '$lib/types';
|
import { toBase64, fromBase64 } from 'js-base64';
|
||||||
|
import { persist, localStorage } from '@macfja/svelte-persistent-store';
|
||||||
|
import type { State } from '$lib/types';
|
||||||
|
import { saveStatistics } from './stats';
|
||||||
|
|
||||||
export const defaultState: State = {
|
export const defaultState: State = {
|
||||||
code: `graph TD
|
code: `graph TD
|
||||||
@@ -38,50 +36,18 @@ const urlParseFailedState = `graph TD
|
|||||||
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
G --> |"No :("| H(Try using the Timeline tab in History <br/>from same browser you used to create the diagram.)
|
||||||
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
click D href "https://github.com/mermaid-js/mermaid-live-editor/issues/new?assignees=&labels=bug&template=bug_report.md&title=Broken%20link" "Raise issue"`;
|
||||||
|
|
||||||
// inputStateStore handles all updates and is shared externally when exporting via URL, History, etc.
|
export const codeStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
||||||
export const inputStateStore = persist(writable(defaultState), localStorage(), 'codeStore');
|
export const base64State: Readable<string> = derived([codeStore], ([code], set) => {
|
||||||
|
set(toBase64(JSON.stringify(code), true));
|
||||||
// All internal reads should be done via stateStore, but it should not be persisted/shared externally.
|
|
||||||
export const stateStore: Readable<ValidatedState> = derived([inputStateStore], ([state]) => {
|
|
||||||
const processed: ValidatedState = {
|
|
||||||
...state,
|
|
||||||
serialized: '',
|
|
||||||
errorMarkers: [],
|
|
||||||
error: undefined
|
|
||||||
};
|
|
||||||
// No changes should be done to fields part of `state`.
|
|
||||||
try {
|
|
||||||
processed.serialized = serializeState(state);
|
|
||||||
mermaid.parse(state.code);
|
|
||||||
JSON.parse(state.mermaid);
|
|
||||||
} catch (e) {
|
|
||||||
processed.error = e;
|
|
||||||
console.error(e);
|
|
||||||
if (e.hash) {
|
|
||||||
try {
|
|
||||||
const marker: MarkerData = {
|
|
||||||
severity: 8, // Error
|
|
||||||
startLineNumber: e.hash.loc.first_line,
|
|
||||||
startColumn: e.hash.loc.first_column,
|
|
||||||
endLineNumber: e.hash.loc.last_line,
|
|
||||||
endColumn: (e.hash.loc.last_column as number) + 1,
|
|
||||||
message: e.str
|
|
||||||
};
|
|
||||||
processed.errorMarkers = [marker];
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error without line helper', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return processed;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const loadState = (data: string): void => {
|
export const loadState = (data: string): void => {
|
||||||
let state: State;
|
let state: State;
|
||||||
console.log('Loading', data);
|
|
||||||
try {
|
try {
|
||||||
state = deserializeState(data);
|
const stateStr = fromBase64(data);
|
||||||
const mermaidConfig: { [key: string]: string } =
|
console.log(`Trying to load state: ${stateStr}`);
|
||||||
|
state = JSON.parse(stateStr);
|
||||||
|
const mermaidConfig =
|
||||||
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
|
typeof state.mermaid === 'string' ? JSON.parse(state.mermaid) : state.mermaid;
|
||||||
if (
|
if (
|
||||||
mermaidConfig.securityLevel &&
|
mermaidConfig.securityLevel &&
|
||||||
@@ -92,33 +58,30 @@ export const loadState = (data: string): void => {
|
|||||||
) {
|
) {
|
||||||
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
|
delete mermaidConfig.securityLevel; // Prevent setting overriding securityLevel when loading state to mitigate possible XSS attack
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
|
state.mermaid = JSON.stringify(mermaidConfig, null, 2);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
state = get(inputStateStore);
|
state = get(codeStore);
|
||||||
if (data) {
|
if (data) {
|
||||||
console.error('Init error', e);
|
console.error('Init error', e);
|
||||||
state.code = urlParseFailedState;
|
state.code = urlParseFailedState;
|
||||||
state.mermaid = defaultState.mermaid;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateCodeStore({ ...state, updateEditor: true });
|
updateCodeStore({ ...state, updateEditor: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateCodeStore = (newState: State): void => {
|
export const updateCodeStore = (newState: State): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, ...newState };
|
return { ...state, ...newState };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
let prompted = false;
|
let prompted = false;
|
||||||
export const updateCode = (
|
export const updateCode = (code: string, updateEditor: boolean, updateDiagram = false): void => {
|
||||||
code: string,
|
|
||||||
{ updateEditor, updateDiagram = false }: { updateEditor: boolean; updateDiagram?: boolean }
|
|
||||||
): void => {
|
|
||||||
saveStatistics(code);
|
saveStatistics(code);
|
||||||
const lines = (code.match(/\n/g) || '').length + 1;
|
const lines = (code.match(/\n/g) || '').length + 1;
|
||||||
|
|
||||||
if (lines > 50 && !prompted && get(stateStore).autoSync) {
|
if (lines > 50 && !prompted && get(codeStore).autoSync) {
|
||||||
const turnOff = confirm(
|
const turnOff = confirm(
|
||||||
'Long diagram detected. Turn off Auto Sync? Click the sync logo to manually sync.'
|
'Long diagram detected. Turn off Auto Sync? Click the sync logo to manually sync.'
|
||||||
);
|
);
|
||||||
@@ -130,19 +93,19 @@ export const updateCode = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, code, updateEditor, updateDiagram };
|
return { ...state, code, updateEditor, updateDiagram };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
export const updateConfig = (config: string, updateEditor: boolean): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
return { ...state, mermaid: config, updateEditor };
|
return { ...state, mermaid: config, updateEditor };
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toggleDarkTheme = (dark: boolean): void => {
|
export const toggleDarkTheme = (dark: boolean): void => {
|
||||||
inputStateStore.update((state) => {
|
codeStore.update((state) => {
|
||||||
const config = JSON.parse(state.mermaid);
|
const config = JSON.parse(state.mermaid);
|
||||||
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
|
if (!config.theme || ['dark', 'default'].includes(config.theme)) {
|
||||||
config.theme = dark ? 'dark' : 'default';
|
config.theme = dark ? 'dark' : 'default';
|
||||||
@@ -153,11 +116,11 @@ export const toggleDarkTheme = (dark: boolean): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initURLSubscription = (): void => {
|
export const initURLSubscription = (): void => {
|
||||||
stateStore.subscribe(({ serialized }) => {
|
base64State.subscribe((state: string) => {
|
||||||
history.replaceState(undefined, undefined, `#${serialized}`);
|
history.replaceState(undefined, undefined, `#${state}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStateString = (): string => {
|
export const getStateString = (): string => {
|
||||||
return JSON.stringify(get(inputStateStore));
|
return JSON.stringify(get(codeStore));
|
||||||
};
|
};
|
||||||
|
|||||||
+69
-89
@@ -1,91 +1,46 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Editor from '$lib/components/editor.svelte';
|
import Editor from '$lib/components/editor/editor.svelte';
|
||||||
import Navbar from '$lib/components/navbar.svelte';
|
import Navbar from '$lib/components/navbar.svelte';
|
||||||
import Preset from '$lib/components/preset.svelte';
|
import Preset from '$lib/components/preset.svelte';
|
||||||
import Actions from '$lib/components/actions.svelte';
|
import Actions from '$lib/components/actions.svelte';
|
||||||
import View from '$lib/components/view.svelte';
|
import View from '$lib/components/view.svelte';
|
||||||
import Card from '$lib/components/card/card.svelte';
|
import Card from '$lib/components/card/card.svelte';
|
||||||
import History from '$lib/components/history/history.svelte';
|
import History from '$lib/components/history/history.svelte';
|
||||||
import { updateCode, updateConfig, inputStateStore, stateStore } from '$lib/util/state';
|
import { updateCode, updateConfig, codeStore, base64State } from '$lib/util/state';
|
||||||
import { initHandler, syncDiagram } from '$lib/util/util';
|
import { initHandler, syncDiagram } from '$lib/util/util';
|
||||||
|
import { errorStore } from '$lib/util/error';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import type { EditorUpdateEvent, State, Tab, DocConfig } from '$lib/types';
|
import mermaid from 'mermaid';
|
||||||
|
import type monaco from 'monaco-editor';
|
||||||
|
import type { EditorUpdateEvent, State, Tab } from '$lib/types';
|
||||||
import { base } from '$app/paths';
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
type Modes = 'code' | 'config';
|
base64State; // Weird fix for error > base64State is not defined. Treeshaking?
|
||||||
type Languages = 'mermaid' | 'json';
|
let selectedMode = 'code';
|
||||||
|
const languageMap = {
|
||||||
let selectedMode: Modes = 'code';
|
|
||||||
const languageMap: { [key in Modes]: Languages } = {
|
|
||||||
code: 'mermaid',
|
code: 'mermaid',
|
||||||
config: 'json'
|
config: 'json'
|
||||||
};
|
};
|
||||||
const docURLBase = 'https://mermaid-js.github.io/mermaid';
|
|
||||||
const docMap: DocConfig = {
|
|
||||||
graph: {
|
|
||||||
code: '/#/flowchart',
|
|
||||||
config: '/#/flowchart?id=configuration'
|
|
||||||
},
|
|
||||||
flowchart: {
|
|
||||||
code: '/#/flowchart',
|
|
||||||
config: '/#/flowchart?id=configuration'
|
|
||||||
},
|
|
||||||
sequenceDiagram: {
|
|
||||||
code: '/#/sequenceDiagram',
|
|
||||||
config: '/#/sequenceDiagram?id=configuration'
|
|
||||||
},
|
|
||||||
classDiagram: {
|
|
||||||
code: '/#/classDiagram',
|
|
||||||
config: '/#/classDiagram?id=configuration'
|
|
||||||
},
|
|
||||||
'stateDiagram-v2': {
|
|
||||||
code: '/#/stateDiagram'
|
|
||||||
},
|
|
||||||
gantt: {
|
|
||||||
code: '/#/gantt',
|
|
||||||
config: '/#/gantt?id=configuration'
|
|
||||||
},
|
|
||||||
pie: {
|
|
||||||
code: '/#/pie'
|
|
||||||
},
|
|
||||||
erDiagram: {
|
|
||||||
code: '/#/entityRelationshipDiagram',
|
|
||||||
config: '/#/entityRelationshipDiagram?id=styling'
|
|
||||||
},
|
|
||||||
journey: {
|
|
||||||
code: '/#/user-journey'
|
|
||||||
},
|
|
||||||
gitGraph: {
|
|
||||||
code: '/#/gitgraph',
|
|
||||||
config: '/#/gitgraph?id=gitgraph-specific-configuration-options'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let text = '';
|
let text = '';
|
||||||
let docURL = docURLBase;
|
let language: 'mermaid' | 'json' = 'mermaid';
|
||||||
let language: Languages = 'mermaid';
|
let errorMarkers: monaco.editor.IMarkerData[] = [];
|
||||||
$: language = languageMap[selectedMode];
|
$: language = languageMap[selectedMode];
|
||||||
$: {
|
$: {
|
||||||
if (selectedMode === 'code') {
|
if (selectedMode === 'code') {
|
||||||
text = $stateStore.code;
|
text = $codeStore.code;
|
||||||
} else {
|
} else {
|
||||||
text = $stateStore.mermaid;
|
text = $codeStore.mermaid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateStore.subscribe((state: State) => {
|
codeStore.subscribe((state: State) => {
|
||||||
if (state.updateEditor) {
|
if (state.updateEditor) {
|
||||||
text = selectedMode === 'code' ? state.code : state.mermaid;
|
text = selectedMode === 'code' ? state.code : state.mermaid;
|
||||||
}
|
}
|
||||||
const codeTypeMatch = /([\S]+)[\s\n]/.exec(state.code);
|
|
||||||
if (codeTypeMatch && codeTypeMatch.length > 1) {
|
|
||||||
const docKey = codeTypeMatch[1];
|
|
||||||
const docConfig = docMap[docKey] ?? { code: '' };
|
|
||||||
docURL = docURLBase + (docConfig[selectedMode] ?? docConfig.code ?? '');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
const tabSelectHandler = (message: CustomEvent<Tab>) => {
|
||||||
selectedMode = message.detail.id === 'code' ? 'code' : 'config';
|
$codeStore.updateEditor = true;
|
||||||
$inputStateStore.updateEditor = true;
|
selectedMode = message.detail.id;
|
||||||
};
|
};
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
{
|
{
|
||||||
@@ -100,26 +55,55 @@
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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>) => {
|
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
|
||||||
const code = message.detail.text;
|
try {
|
||||||
if (selectedMode === 'code') {
|
if (selectedMode === 'code') {
|
||||||
updateCode(code, {
|
handleCodeUpdate(message.detail.text);
|
||||||
updateEditor: false
|
} else {
|
||||||
});
|
handleConfigUpdate(message.detail.text);
|
||||||
} else {
|
}
|
||||||
updateConfig(code, false);
|
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 = () => {
|
const viewDiagram = () => {
|
||||||
window.open(`${base}/view#${$stateStore.serialized}`, '_blank').focus();
|
window.open(`${base}/view#${$base64State}`, '_blank').focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await initHandler();
|
await initHandler();
|
||||||
const resizer = document.getElementById('resizeHandler');
|
const resizer = document.getElementById('resizeHandler');
|
||||||
const element = document.getElementById('editorPane');
|
const element = document.getElementById('editorPane');
|
||||||
const resize = (e: { pageX: number }) => {
|
const resize = (e) => {
|
||||||
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
const newWidth = e.pageX - element.getBoundingClientRect().left;
|
||||||
if (newWidth > 50) {
|
if (newWidth > 50) {
|
||||||
element.style.width = `${newWidth}px`;
|
element.style.width = `${newWidth}px`;
|
||||||
@@ -144,32 +128,28 @@
|
|||||||
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
|
<Card on:select={tabSelectHandler} {tabs} isCloseable={false} title="Mermaid">
|
||||||
<div slot="actions">
|
<div slot="actions">
|
||||||
<div class="flex flex-row items-center">
|
<div class="flex flex-row items-center">
|
||||||
<div class="form-control flex-row items-center">
|
{#if !$codeStore.autoSync}
|
||||||
<label class="cursor-pointer label" for="autoSync">
|
|
||||||
<span> Auto sync</span>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="toggle {$stateStore.autoSync ? 'btn-secondary' : 'toggle-primary'} ml-1"
|
|
||||||
id="autoSync"
|
|
||||||
bind:checked={$inputStateStore.autoSync} />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if !$stateStore.autoSync}
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary btn-xs mr-1"
|
class="btn btn-secondary btn-xs"
|
||||||
title="Sync Diagram"
|
title="Sync Diagram"
|
||||||
data-cy="sync"
|
data-cy="sync"
|
||||||
on:click={syncDiagram}><i class="fas fa-sync" /></button>
|
on:click={syncDiagram}><i class="fas fa-sync" /></button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button class="btn btn-secondary btn-xs" title="View documentation">
|
<div class="form-control">
|
||||||
<a target="_blank" href={docURL} data-cy="docs"><i class="fas fa-book mr-1" />Docs</a>
|
<label class="cursor-pointer label" for="autoSync">
|
||||||
</button>
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle toggle-primary mr-1"
|
||||||
|
id="autoSync"
|
||||||
|
bind:checked={$codeStore.autoSync} />
|
||||||
|
<span> Auto sync</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Editor on:update={updateHandler} {language} bind:text />
|
<Editor on:update={updateHandler} {language} bind:text {errorMarkers} />
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<div class="-mt-2">
|
<div class="-mt-2">
|
||||||
@@ -183,9 +163,9 @@
|
|||||||
<Card title="Diagram" isCloseable={false}>
|
<Card title="Diagram" isCloseable={false}>
|
||||||
<button
|
<button
|
||||||
slot="actions"
|
slot="actions"
|
||||||
class="btn btn-secondary btn-xs"
|
class="btn btn-primary btn-xs shadow-lg"
|
||||||
title="View diagram in new page"
|
title="View diagram in new page"
|
||||||
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye mr-1" />View</button>
|
on:click|stopPropagation={() => viewDiagram()}><i class="far fa-eye mr-2" /> View</button>
|
||||||
|
|
||||||
<div class="flex-1 overflow-auto">
|
<div class="flex-1 overflow-auto">
|
||||||
<View />
|
<View />
|
||||||
|
|||||||
+5
-5
@@ -12,20 +12,20 @@ const config = {
|
|||||||
],
|
],
|
||||||
kit: {
|
kit: {
|
||||||
adapter: adapter({
|
adapter: adapter({
|
||||||
pages: `docs`
|
pages: `docs${process.env['BETA'] ? '/beta' : ''}`
|
||||||
}),
|
}),
|
||||||
paths: process.env['DEPLOY']
|
paths: process.env['DEPLOY']
|
||||||
? {
|
? {
|
||||||
base: `/mermaid-live-editor`
|
base: `/mermaid-live-editor${process.env['BETA'] ? '/beta' : ''}`
|
||||||
}
|
}
|
||||||
: {},
|
: {},
|
||||||
|
ssr: false,
|
||||||
|
// hydrate the <div id="svelte"> element in src/app.html
|
||||||
|
target: '#svelte',
|
||||||
trailingSlash: 'ignore',
|
trailingSlash: 'ignore',
|
||||||
vite: {
|
vite: {
|
||||||
envPrefix: 'MERMAID_',
|
envPrefix: 'MERMAID_',
|
||||||
optimizeDeps: { include: ['mermaid'] }
|
optimizeDeps: { include: ['mermaid'] }
|
||||||
},
|
|
||||||
prerender: {
|
|
||||||
default: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
mode: 'jit',
|
||||||
plugins: [require('daisyui')],
|
plugins: [require('daisyui')],
|
||||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
+29
-5
@@ -1,14 +1,38 @@
|
|||||||
{
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"module": "es2020",
|
||||||
|
"lib": ["es2020", "ESNext", "DOM.Iterable"],
|
||||||
|
"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": [
|
"include": [
|
||||||
"src/**/*.d.ts",
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.js",
|
||||||
"src/**/*.ts",
|
"src/**/*.ts",
|
||||||
"src/**/*.svelte",
|
"src/**/*.svelte",
|
||||||
"cypress/**/*.ts",
|
"cypress/**/*.ts",
|
||||||
"cypress/**/*.js",
|
"cypress/**/*.js",
|
||||||
"static/**/*.js"
|
"static/**/*.js"
|
||||||
],
|
]
|
||||||
"compilerOptions": {
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"extends": "./.svelte-kit/tsconfig.json"
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user