diff --git a/package.json b/package.json index 183ce842..1108933a 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "devDependencies": { "@sveltejs/kit": "next", + "@types/mermaid": "^8.2.5", "@typescript-eslint/eslint-plugin": "^4.19.0", "@typescript-eslint/parser": "^4.19.0", "autoprefixer": "^10.2.5", @@ -31,6 +32,7 @@ "dependencies": { "@fontsource/fira-mono": "^4.2.2", "@lukeed/uuid": "^2.0.0", + "@tailwindcss/forms": "^0.3.2", "cookie": "^0.4.1", "js-base64": "^3.6.0", "mermaid": "^8.9.3", diff --git a/src/lib/Editor/index.svelte b/src/lib/Editor/index.svelte index 76572691..2e6a287a 100644 --- a/src/lib/Editor/index.svelte +++ b/src/lib/Editor/index.svelte @@ -6,15 +6,22 @@ import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; + import { initEditor } from './util'; + let divEl: HTMLDivElement = null; let editor: monaco.editor.IStandaloneCodeEditor; let Monaco; + + export let text: string; + export let language: string; export let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = { - value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'), - language: 'javascript' - // automaticLayout: true + value: text, + language: language }; + $: Monaco?.editor.setModelLanguage(editor.getModel(), language); + $: editor?.setValue(text); + const dispatch = createEventDispatcher(); onMount(async () => { @@ -37,6 +44,8 @@ } }; Monaco = await import('monaco-editor'); + initEditor(Monaco); + // divEl = document.getElementById('editor') as HTMLDivElement; editor = Monaco.editor.create(divEl, editorOptions); editor.onDidChangeModelContent(async () => { diff --git a/src/lib/Editor/util.js b/src/lib/Editor/util.js new file mode 100644 index 00000000..c3458898 --- /dev/null +++ b/src/lib/Editor/util.js @@ -0,0 +1,75 @@ +export const initEditor = (monaco) => { + monaco.languages.register({ id: 'mermaid' }); + + // Register a tokens provider for the language + monaco.languages.setMonarchTokensProvider('mermaid', { + typeKeywords: [ + 'graph', + 'stateDiagram', + 'sequenceDiagram', + 'classDiagram', + 'pie', + 'flowchart', + 'gantt' + ], + keywords: ['patricipant', 'as'], + arrows: ['---', '===', '-->', '==>'], + + tokenizer: { + root: [ + [/[{}]/, 'delimiter.bracket'], + [/[a-z_$][\w$]*/, { cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } }], + [/[-=>ox]+/, { cases: { '@arrows': 'transition' } }], + [/[\[\{\(}]+.+?[\)\]\}]+/, 'string'], + [/\".*\"/, 'string'] + ] + }, + whitespace: [ + [/[ \t\r\n]+/, 'white'], + [/\%\%.*$/, 'comment'] + ] + }); + + monaco.editor.defineTheme('myCoolTheme', { + base: 'vs', + inherit: false, + rules: [ + { token: 'keyword', foreground: '880000', fontStyle: 'bold' }, + { token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' }, + { token: 'string', foreground: 'AA8500' }, + { token: 'transition', foreground: '008800', fontStyle: 'bold' }, + { token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' } + ] + }); + + // Register a completion item provider for the new language + monaco.languages.registerCompletionItemProvider('mermaid', { + provideCompletionItems: () => { + var suggestions = [ + { + label: 'simpleText', + kind: monaco.languages.CompletionItemKind.Text, + insertText: 'simpleText' + }, + { + label: 'testing', + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: 'testing(${1:condition})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet + }, + { + label: 'ifelse', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'), + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + documentation: 'If-Else Statement' + } + ]; + return { suggestions: suggestions }; + } + }); +}; + +export const getResizeHandler = (editor) => { + return (node) => editor && editor.layout(); +}; diff --git a/src/lib/Util/error.ts b/src/lib/Util/error.ts new file mode 100644 index 00000000..43829bf6 --- /dev/null +++ b/src/lib/Util/error.ts @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store'; + +export const errorStore = writable(undefined); diff --git a/src/lib/Util/mermaid.ts b/src/lib/Util/mermaid.ts new file mode 100644 index 00000000..ee5b14d3 --- /dev/null +++ b/src/lib/Util/mermaid.ts @@ -0,0 +1,9 @@ +import type { Mermaid } from 'mermaid'; + +let mer: Mermaid; +export const getMermaid = async (): Promise => { + if (!mer) { + mer = await import('mermaid'); + } + return mer; +}; diff --git a/src/lib/Util/state.ts b/src/lib/Util/state.ts index c335e413..6990b82c 100644 --- a/src/lib/Util/state.ts +++ b/src/lib/Util/state.ts @@ -9,7 +9,9 @@ const defaultState: State = { C -->|Two| E[iPhone] C -->|Three| F[fa:fa-car Car] `, - mermaid: 'default', + mermaid: JSON.stringify({ + theme: 'default' + }), updateEditor: false }; @@ -17,6 +19,7 @@ export const codeStore = writable(defaultState); export const loadState = (data: string): void => { let state: State; + // debugger; try { const stateStr = decode(data); console.log('state from url', stateStr); @@ -38,7 +41,7 @@ export const updateCode = (code: string, updateEditor: boolean): void => { }); }; -export const updateConfig = (config: any, updateEditor: boolean): void => { +export const updateConfig = (config: string, updateEditor: boolean): void => { codeStore.update((state) => { return { ...state, mermaid: config, updateEditor }; }); diff --git a/src/lib/Util/util.ts b/src/lib/Util/util.ts index 6288d9f4..16cc4754 100644 --- a/src/lib/Util/util.ts +++ b/src/lib/Util/util.ts @@ -1,6 +1,5 @@ import { loadState } from './state'; export const loadStateFromURL = (): void => { - debugger; - loadState(window.location.hash); + loadState(window.location.hash.slice(1)); }; diff --git a/src/lib/View/index.svelte b/src/lib/View/index.svelte new file mode 100644 index 00000000..86526515 --- /dev/null +++ b/src/lib/View/index.svelte @@ -0,0 +1,54 @@ + + +
+
+
+ + diff --git a/src/routes/edit.svelte b/src/routes/edit.svelte index 96f18fe3..b915fdf1 100644 --- a/src/routes/edit.svelte +++ b/src/routes/edit.svelte @@ -1,17 +1,38 @@ @@ -36,18 +80,33 @@ Edit -
- -
- -
+
+
+ +
+
+
+
+
+ {#if !autoSync} + + {/if} + +
+
+
- {#if selectedTab == 'code'} - - {:else} - - {/if} - + + +
- +
+ +
Diagram
+
+
diff --git a/svelte.config.js b/svelte.config.js index 71f5aa44..4aee00f4 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -7,12 +7,20 @@ const config = { preprocess: [ preprocess({ postcss: true - }), + }) ], kit: { // hydrate the
element in src/app.html - target: '#svelte' + target: '#svelte', + vite: { + // ssr: { + // noExternal: [] + // }, + optimizeDeps: { + include: ['mermaid'] + } + } } }; diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 239aeca2..fb6a711e 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,26 +1,26 @@ -const { tailwindExtractor } = require("tailwindcss/lib/lib/purgeUnusedStyles"); +const { tailwindExtractor } = require('tailwindcss/lib/lib/purgeUnusedStyles'); module.exports = { - mode: "aot", + mode: 'aot', purge: { - content: [ - "./src/**/*.{html,js,svelte,ts}", - ], + content: ['./src/**/*.{html,js,svelte,ts}'], options: { defaultExtractor: (content) => [ // If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it ...tailwindExtractor(content), // Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731) - ...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(([_match, group, ..._rest]) => group), - ], + ...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map( + ([_match, group, ..._rest]) => group + ) + ] }, - safelist: [/^svelte-[\d\w]+$/], + safelist: [/^svelte-[\d\w]+$/] }, theme: { - extend: {}, + extend: {} }, variants: { - extend: {}, + extend: {} }, - plugins: [], + plugins: [require('@tailwindcss/forms')] }; diff --git a/yarn.lock b/yarn.lock index 6e5b3c1a..01d76f3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -127,6 +127,13 @@ source-map "^0.7.3" svelte-hmr "^0.14.2" +"@tailwindcss/forms@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.3.2.tgz#e28c4514a53e69f725416a5a2a6d0f221683f069" + integrity sha512-aj2/rJsGb2whAZ/BQWHWWQRSbhH0r/l1ozOByiv+ZNjBD84GMvb5dhAyfpeasFky+EJrAwX5eaqft8NQMZFWvA== + dependencies: + mini-svg-data-uri "^1.2.3" + "@trysound/sax@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669" @@ -137,6 +144,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/mermaid@^8.2.5": + version "8.2.5" + resolved "https://registry.yarnpkg.com/@types/mermaid/-/mermaid-8.2.5.tgz#85ef5be8a1532c0f596a920972a6242f009a6ce8" + integrity sha512-sVKakt9BN2T2qaEHsmQPtV77QAyu5kZh0CErCEaUW4ThBn4VJ47xYuLuvJttmeuSeKfdP/ACi5h184H4gWt4gA== + "@types/node@*": version "15.0.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67" @@ -1765,6 +1777,11 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +mini-svg-data-uri@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz#e16baa92ad55ddaa1c2c135759129f41910bc39f" + integrity sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ== + minify@^4.1.1: version "4.1.3" resolved "https://registry.yarnpkg.com/minify/-/minify-4.1.3.tgz#58467922d14303f55a3a28fa79641371955b8fbd"