Allow Note Edit
authorAdam Dullage <redacted>
Wed, 11 Aug 2021 12:33:38 +0000 (13:33 +0100)
committerAdam Dullage <redacted>
Wed, 11 Aug 2021 12:33:38 +0000 (13:33 +0100)
12 files changed:
Pipfile.lock
README.md
flatnotes/flatnotes.py
flatnotes/main.py
flatnotes/src/App.vue [deleted file]
flatnotes/src/components/App.js [moved from flatnotes/src/App.js with 53% similarity]
flatnotes/src/components/App.vue [new file with mode: 0644]
flatnotes/src/components/classes.js [moved from flatnotes/src/classes.js with 87% similarity]
flatnotes/src/index.js
flatnotes/src/main.scss [new file with mode: 0644]
package-lock.json
package.json

index d79cae4af6e0989d5f970767c32e802489c1eb18..690fdf2cab0c986d7ee1661520ec5ee43e4fd1ac 100644 (file)
         },
         "regex": {
             "hashes": [
-                "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f",
-                "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad",
-                "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a",
-                "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf",
-                "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59",
-                "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d",
-                "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895",
-                "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4",
-                "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3",
-                "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222",
-                "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0",
-                "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c",
-                "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417",
-                "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d",
-                "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d",
-                "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761",
-                "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0",
-                "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026",
-                "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854",
-                "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb",
-                "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d",
-                "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068",
-                "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde",
-                "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d",
-                "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec",
-                "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa",
-                "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd",
-                "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b",
-                "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26",
-                "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2",
-                "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f",
-                "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694",
-                "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0",
-                "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407",
-                "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874",
-                "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035",
-                "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d",
-                "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c",
-                "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5",
-                "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985",
-                "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58"
+                "sha256:026beb631097a4a3def7299aa5825e05e057de3c6d72b139c37813bfa351274b",
+                "sha256:14caacd1853e40103f59571f169704367e79fb78fac3d6d09ac84d9197cadd16",
+                "sha256:16d9eaa8c7e91537516c20da37db975f09ac2e7772a0694b245076c6d68f85da",
+                "sha256:18fdc51458abc0a974822333bd3a932d4e06ba2a3243e9a1da305668bd62ec6d",
+                "sha256:28e8af338240b6f39713a34e337c3813047896ace09d51593d6907c66c0708ba",
+                "sha256:3835de96524a7b6869a6c710b26c90e94558c31006e96ca3cf6af6751b27dca1",
+                "sha256:3905c86cc4ab6d71635d6419a6f8d972cab7c634539bba6053c47354fd04452c",
+                "sha256:3c09d88a07483231119f5017904db8f60ad67906efac3f1baa31b9b7f7cca281",
+                "sha256:4551728b767f35f86b8e5ec19a363df87450c7376d7419c3cac5b9ceb4bce576",
+                "sha256:459bbe342c5b2dec5c5223e7c363f291558bc27982ef39ffd6569e8c082bdc83",
+                "sha256:4f421e3cdd3a273bace013751c345f4ebeef08f05e8c10757533ada360b51a39",
+                "sha256:577737ec3d4c195c4aef01b757905779a9e9aee608fa1cf0aec16b5576c893d3",
+                "sha256:57fece29f7cc55d882fe282d9de52f2f522bb85290555b49394102f3621751ee",
+                "sha256:7976d410e42be9ae7458c1816a416218364e06e162b82e42f7060737e711d9ce",
+                "sha256:85f568892422a0e96235eb8ea6c5a41c8ccbf55576a2260c0160800dbd7c4f20",
+                "sha256:8764a78c5464ac6bde91a8c87dd718c27c1cabb7ed2b4beaf36d3e8e390567f9",
+                "sha256:8935937dad2c9b369c3d932b0edbc52a62647c2afb2fafc0c280f14a8bf56a6a",
+                "sha256:8fe58d9f6e3d1abf690174fd75800fda9bdc23d2a287e77758dc0e8567e38ce6",
+                "sha256:937b20955806381e08e54bd9d71f83276d1f883264808521b70b33d98e4dec5d",
+                "sha256:9569da9e78f0947b249370cb8fadf1015a193c359e7e442ac9ecc585d937f08d",
+                "sha256:a3b73390511edd2db2d34ff09aa0b2c08be974c71b4c0505b4a048d5dc128c2b",
+                "sha256:a4eddbe2a715b2dd3849afbdeacf1cc283160b24e09baf64fa5675f51940419d",
+                "sha256:a5c6dbe09aff091adfa8c7cfc1a0e83fdb8021ddb2c183512775a14f1435fe16",
+                "sha256:b63e3571b24a7959017573b6455e05b675050bbbea69408f35f3cb984ec54363",
+                "sha256:bb350eb1060591d8e89d6bac4713d41006cd4d479f5e11db334a48ff8999512f",
+                "sha256:bf6d987edd4a44dd2fa2723fca2790f9442ae4de2c8438e53fcb1befdf5d823a",
+                "sha256:bfa6a679410b394600eafd16336b2ce8de43e9b13f7fb9247d84ef5ad2b45e91",
+                "sha256:c856ec9b42e5af4fe2d8e75970fcc3a2c15925cbcc6e7a9bcb44583b10b95e80",
+                "sha256:cea56288eeda8b7511d507bbe7790d89ae7049daa5f51ae31a35ae3c05408531",
+                "sha256:ea212df6e5d3f60341aef46401d32fcfded85593af1d82b8b4a7a68cd67fdd6b",
+                "sha256:f35567470ee6dbfb946f069ed5f5615b40edcbb5f1e6e1d3d2b114468d505fc6",
+                "sha256:fbc20975eee093efa2071de80df7f972b7b35e560b213aafabcec7c0bd00bd8c",
+                "sha256:ff4a8ad9638b7ca52313d8732f37ecd5fd3c8e3aff10a8ccb93176fd5b3812f6"
             ],
-            "version": "==2021.7.6"
+            "version": "==2021.8.3"
         },
         "rope": {
             "hashes": [
index 12d3e43f656b0d63a23286b60d14741c8050daa0..8600e511196f18412d4ce173e581211abc46fcf1 100644 (file)
--- a/README.md
+++ b/README.md
@@ -31,8 +31,7 @@ This is what flatnotes aims to achieve.
   * [x] Full Text Searching
 * [ ] Proof of Concept - Stage 2
   * [x] View Note Content
-  * [ ] Edit Note Content
-  * [ ] Create New Note
+  * [x] Edit Note Content
   * [ ] Docker Deployment
   * [ ] Password Authentication
 * [ ] Proof of Concept - Stage 3
@@ -42,6 +41,7 @@ This is what flatnotes aims to achieve.
   * [ ] Public URL Sharing
 * [ ] First Release
   * [ ] Clean & Responsive UI
-  * [ ] Ability to Delete a Note
+  * [ ] Ability to Create a Note
   * [ ] Ability to Rename a Note
+  * [ ] Ability to Delete a Note
   * [ ] Error Handling
index 54c7df9ab3b21a504330c75908b9acf62253f119..5be1dd32aa65cc906e1f165abcdd6140081526c0 100644 (file)
@@ -66,14 +66,14 @@ class Note:
 
     @property
     def content(self):
-        with open(self.filepath, "r") as f:
+        with open(self.filepath, "r", encoding="utf-8") as f:
             return f.read()
 
     @content.setter
     def content(self, new_content):
         if not os.path.exists(self.filepath):
             raise FileNotFoundError
-        with open(self.filepath, "w") as f:
+        with open(self.filepath, "w", encoding="utf-8") as f:
             f.write(new_content)
 
     def delete(self):
index f6f7b08ad88dfc4cd524eb61e2d72f77b9682f26..4ac174d4889d13cc611f60df624f4b63047de84c 100644 (file)
@@ -3,7 +3,7 @@ import os
 from typing import Dict, List, Optional
 
 from fastapi import FastAPI
-from fastapi.responses import RedirectResponse
+from fastapi.responses import HTMLResponse
 from fastapi.staticfiles import StaticFiles
 
 from error_responses import (
@@ -40,6 +40,11 @@ class NoteModel(CamelCaseBaseModel):
         }
 
 
+class NotePatchModel(CamelCaseBaseModel):
+    new_filename: Optional[str]
+    new_content: Optional[str]
+
+
 class NoteHitModel(CamelCaseBaseModel):
     filename: str
     last_modified: int
@@ -58,7 +63,9 @@ class NoteHitModel(CamelCaseBaseModel):
 
 @app.get("/")
 async def root():
-    return RedirectResponse("/index.html")
+    with open("flatnotes/dist/index.html", "r", encoding="utf-8") as f:
+        html = f.read()
+    return HTMLResponse(content=html)
 
 
 @app.get("/api/notes", response_model=List[NoteModel])
@@ -96,15 +103,13 @@ async def get_note(filename: str, include_content: bool = True):
 
 
 @app.patch("/api/notes/{filename}", response_model=NoteModel)
-async def patch_note(
-    filename: str, new_filename: str = None, new_content: str = None
-):
+async def patch_note(filename: str, new_data: NotePatchModel):
     try:
         note = Note(flatnotes, filename)
-        if new_filename is not None:
-            note.filename = new_filename
-        if new_content is not None:
-            note.content = new_content
+        if new_data.new_filename is not None:
+            note.filename = new_data.new_filename
+        if new_data.new_content is not None:
+            note.content = new_data.new_content
         return NoteModel.dump(note, include_content=True)
     except FilenameContainsPathError:
         return filename_contains_path_response
diff --git a/flatnotes/src/App.vue b/flatnotes/src/App.vue
deleted file mode 100644 (file)
index e292e15..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<template>
-  <div class="container">
-    <div class="row">
-      <div class="col"></div>
-      <div class="col-6">
-        <!-- Header -->
-        <div class="mt-4 mb-4">
-          <h1 class="text-center">flatnotes</h1>
-        </div>
-
-        <!-- Search Input -->
-        <div class="form-group mb-4">
-          <input
-            type="text"
-            class="form-control"
-            placeholder="Search"
-            v-model="searchTerm"
-            @change="search"
-          />
-        </div>
-
-        <!-- Search Results -->
-        <div v-if="searchTerm">
-          <div
-            v-for="result in searchResults"
-            :key="result.filename"
-            class="mb-5"
-          >
-            <p
-              class="h5 text-center"
-              v-html="result.titleHighlightsOrTitle"
-            ></p>
-            <p
-              class="text-center text-muted"
-              v-html="result.contentHighlights"
-            ></p>
-          </div>
-        </div>
-
-        <!-- Notes -->
-        <div v-else>
-          <p
-            v-for="note in notesByLastModifiedDesc"
-            :key="note.filename"
-            class="text-center"
-          >
-            {{ note.title }}
-          </p>
-        </div>
-      </div>
-      <div class="col"></div>
-    </div>
-  </div>
-</template>
-
-<script>
-export { default } from "./App.js";
-</script>
similarity index 53%
rename from flatnotes/src/App.js
rename to flatnotes/src/components/App.js
index 3935d1a14fcd2ecfb05980fbc73a8585a558385d..c40229ba2a2233dc3a360a96696d965723968972 100644 (file)
@@ -1,17 +1,48 @@
+import "@toast-ui/editor/dist/toastui-editor.css";
+import "@toast-ui/editor/dist/toastui-editor-viewer.css";
+import { Editor } from "@toast-ui/vue-editor";
+import { Viewer } from "@toast-ui/vue-editor";
 import axios from "axios";
+
 import { Note, SearchResult } from "./classes";
 
 export default {
+  components: {
+    Viewer,
+    Editor,
+  },
+
   data: function() {
     return {
       notes: [],
       searchTerm: null,
       searchTimeout: null,
-      searchResults: [],
+      searchResults: null,
+      currentNote: null,
+      editMode: false,
     };
   },
 
   computed: {
+    currentView: function() {
+      // 3 - Edit Note
+      if (this.currentNote && this.editMode) {
+        return 3;
+      }
+      // 2 - View Note
+      else if (this.currentNote) {
+        return 2;
+      }
+      // 1 - Search Results
+      else if (this.searchResults) {
+        return 1;
+      }
+      // 0 - Notes List
+      else {
+        return 0;
+      }
+    },
+
     notesByLastModifiedDesc: function() {
       return this.notes.sort(function(a, b) {
         return b.lastModified - a.lastModified;
@@ -25,7 +56,7 @@ export default {
       if (this.searchTerm) {
         this.startSearchTimeout();
       } else {
-        this.searchResults = [];
+        this.searchResults = null;
       }
     },
   },
@@ -54,6 +85,7 @@ export default {
 
     search: function() {
       parent = this;
+      this.clearSearchTimeout();
       this.searchResults = [];
       if (this.searchTerm) {
         axios
@@ -72,6 +104,35 @@ export default {
           });
       }
     },
+
+    loadNote: function(filename) {
+      parent = this;
+      axios.get(`/api/notes/${filename}`).then(function(response) {
+        parent.currentNote = response.data;
+      });
+    },
+
+    unloadNote: function() {
+      this.currentNote = null;
+      this.editMode = false;
+    },
+
+    saveNote: function() {
+      parent = this;
+      let newContent = this.$refs.toastUiEditor.invoke("getMarkdown");
+      if (newContent != this.currentNote.content) {
+        axios
+          .patch(`/api/notes/${this.currentNote.filename}`, {
+            newContent: newContent,
+          })
+          .then(function(response) {
+            parent.currentNote = response.data;
+            parent.editMode = false;
+          });
+      } else {
+        this.editMode = false;
+      }
+    },
   },
 
   created: function() {
diff --git a/flatnotes/src/components/App.vue b/flatnotes/src/components/App.vue
new file mode 100644 (file)
index 0000000..6f08ec5
--- /dev/null
@@ -0,0 +1,121 @@
+<template>
+  <div class="container">
+    <div>
+      <!-- Header -->
+      <div class="mt-4 mb-4">
+        <h1 class="text-center">flatnotes</h1>
+      </div>
+
+      <!-- Buttons -->
+      <div
+        v-if="currentView == 2 || currentView == 3"
+        class="d-flex justify-content-center mb-4"
+      >
+        <!-- Close -->
+        <button
+          v-if="currentView == 2"
+          type="button"
+          class="btn btn-secondary"
+          @click="unloadNote"
+        >
+          Close
+        </button>
+
+        <!-- Edit -->
+        <button
+          v-if="currentView == 2"
+          type="button"
+          class="btn btn-warning ms-2"
+          @click="editMode = true"
+        >
+          Edit
+        </button>
+
+        <!-- Cancel -->
+        <button
+          v-if="currentView == 3"
+          type="button"
+          class="btn btn-secondary ms-2"
+          @click="editMode = false"
+        >
+          Cancel
+        </button>
+
+        <!-- Save -->
+        <button
+          v-if="currentView == 3"
+          type="button"
+          class="btn btn-success ms-2"
+          @click="saveNote"
+        >
+          Save
+        </button>
+      </div>
+
+      <!-- Viewer -->
+      <div v-if="currentView == 2">
+        <viewer :initialValue="currentNote.content" height="600px" />
+      </div>
+
+      <!-- Editor -->
+      <div v-else-if="currentView == 3">
+        <editor
+          :initialValue="currentNote.content"
+          previewStyle="tab"
+          height="calc(100vh - 180px)"
+          ref="toastUiEditor"
+        />
+      </div>
+
+      <!-- Front Page -->
+      <div v-else>
+        <!-- Search Input -->
+        <div class="form-group mb-4 d-flex justify-content-center">
+          <input
+            type="text"
+            class="form-control"
+            placeholder="Search"
+            v-model="searchTerm"
+            @change="search"
+            style="max-width: 500px"
+          />
+        </div>
+
+        <!-- Search Results -->
+        <div v-if="currentView == 1">
+          <div
+            v-for="result in searchResults"
+            :key="result.filename"
+            class="mb-5"
+          >
+            <p
+              class="h5 text-center clickable-link"
+              v-html="result.titleHighlightsOrTitle"
+              @click="loadNote(result.filename)"
+            ></p>
+            <p
+              class="text-center text-muted"
+              v-html="result.contentHighlights"
+            ></p>
+          </div>
+        </div>
+
+        <!-- Notes -->
+        <div v-else>
+          <p
+            v-for="note in notesByLastModifiedDesc"
+            :key="note.filename"
+            class="text-center clickable-link mb-2"
+            @click="loadNote(note.filename)"
+          >
+            {{ note.title }}
+          </p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export { default } from "./App.js";
+</script>
similarity index 87%
rename from flatnotes/src/classes.js
rename to flatnotes/src/components/classes.js
index a3b2afc646dffcb6d594b901063118118bf2cade..513fe23521b997c10f17bd72016d18b0f8966ebf 100644 (file)
@@ -1,7 +1,8 @@
 class Note {
-  constructor(filename, lastModified) {
+  constructor(filename, lastModified, content) {
     this.filename = filename;
     this.lastModified = lastModified;
+    this.content = content;
   }
 
   get title() {
index fc389d24823cf9dff94841d4b510ed994c61b1ed..870be7f06ef20146f85f7a848a1d0d89490e9e76 100644 (file)
@@ -1,9 +1,11 @@
-import App from "./App.vue";
+import App from "./components/App.vue";
 import Vue from "vue";
 import { BootstrapVue, IconsPlugin } from "bootstrap-vue";
 import "bootstrap/dist/css/bootstrap.css";
 import "bootstrap-vue/dist/bootstrap-vue.css";
 
+import "./main.scss"
+
 Vue.use(BootstrapVue);
 Vue.use(IconsPlugin);
 
diff --git a/flatnotes/src/main.scss b/flatnotes/src/main.scss
new file mode 100644 (file)
index 0000000..a2185bc
--- /dev/null
@@ -0,0 +1,6 @@
+.clickable-link {
+    cursor: pointer;
+    &:hover {
+        font-weight: bold;
+    }
+}
index f6eb819b4928480800d81d538f3d5b9d965822a9..7bcdb3478c6d639b683a8ded78c7a3009f07ba54 100644 (file)
@@ -6,13 +6,15 @@
   "packages": {
     "": {
       "version": "0.0.0",
-      "license": "UNLICENSED",
+      "license": "MIT",
       "devDependencies": {
+        "@toast-ui/vue-editor": "^3.0.2",
         "@vue/component-compiler-utils": "^3.2.2",
         "axios": "^0.21.1",
         "bootstrap": "^5.0.2",
         "bootstrap-vue": "^2.21.2",
         "parcel-bundler": "^1.12.5",
+        "sass": "^1.37.5",
         "vue": "^2.6.14",
         "vue-hot-reload-api": "^2.3.4",
         "vue-template-compiler": "^2.6.14"
         "url": "https://opencollective.com/popperjs"
       }
     },
+    "node_modules/@toast-ui/editor": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@toast-ui/editor/-/editor-3.0.2.tgz",
+      "integrity": "sha512-auyfN5OzXZoR76FeR9kVKuZctEPj15i9PQOM4ebE1VOWyJ9VPXPwFYDZ1KQNWkxkMWWqw7qI28VDIUQhTvK3mg==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-commands": "^1.1.9",
+        "prosemirror-history": "^1.1.3",
+        "prosemirror-inputrules": "^1.1.3",
+        "prosemirror-keymap": "^1.1.4",
+        "prosemirror-model": "^1.14.1",
+        "prosemirror-state": "^1.3.4",
+        "prosemirror-view": "^1.18.7"
+      }
+    },
+    "node_modules/@toast-ui/vue-editor": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@toast-ui/vue-editor/-/vue-editor-3.0.2.tgz",
+      "integrity": "sha512-16ODRlWGvVpAa8wgI6i93O3Op3kn5BCKDO8fVsQO1KLTgaBCwTYoCE9DWcwPMXgmzCAWEYypnphpXx1M86BJIg==",
+      "dev": true,
+      "dependencies": {
+        "@toast-ui/editor": "^3.0.2"
+      },
+      "peerDependencies": {
+        "vue": "^2.5.0"
+      }
+    },
     "node_modules/@types/q": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
         "node": ">=4"
       }
     },
+    "node_modules/orderedmap": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.1.tgz",
+      "integrity": "sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==",
+      "dev": true
+    },
     "node_modules/os-browserify": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
       "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=",
       "dev": true
     },
+    "node_modules/picomatch": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+      "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
     "node_modules/pn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "node_modules/prosemirror-commands": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.1.10.tgz",
+      "integrity": "sha512-IWyBBXNAd44RM6NnBPljwq+/CM2oYCQJkF+YhKEAZNwzW0uFdGf4qComhjbKZzqFdu6Iub2ZhNsXgwPibA0lCQ==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-history": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.1.3.tgz",
+      "integrity": "sha512-zGDotijea+vnfnyyUGyiy1wfOQhf0B/b6zYcCouBV8yo6JmrE9X23M5q7Nf/nATywEZbgRLG70R4DmfSTC+gfg==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-state": "^1.2.2",
+        "prosemirror-transform": "^1.0.0",
+        "rope-sequence": "^1.3.0"
+      }
+    },
+    "node_modules/prosemirror-inputrules": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
+      "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-keymap": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz",
+      "integrity": "sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-state": "^1.0.0",
+        "w3c-keyname": "^2.2.0"
+      }
+    },
+    "node_modules/prosemirror-model": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.14.3.tgz",
+      "integrity": "sha512-yzZlBaSxfUPIIP6U5Edh5zKxJPZ5f7bwZRhiCuH3UYkWhj+P3d8swHsbuAMOu/iDatDc5J/Qs5Mb3++mZf+CvQ==",
+      "dev": true,
+      "dependencies": {
+        "orderedmap": "^1.1.0"
+      }
+    },
+    "node_modules/prosemirror-state": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
+      "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-transform": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.2.tgz",
+      "integrity": "sha512-/G6d/u9Mf6Bv3H1XR8VxhpjmUO75LYmnvj+s3ZfZpakU1hnQbsvCEybml1B3f2IWUAAQRFkbO1PnsbFhLZsYsw==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-model": "^1.0.0"
+      }
+    },
+    "node_modules/prosemirror-view": {
+      "version": "1.18.11",
+      "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.18.11.tgz",
+      "integrity": "sha512-KXUM8UEV+IK4JYWHNyxkPGDGbxeTEUHQv3POApfyTRN5eMcPFbY4cB0mDJr0LPelVvYPghmZDOCqfCIm9mYHtQ==",
+      "dev": true,
+      "dependencies": {
+        "prosemirror-model": "^1.14.3",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0"
+      }
+    },
     "node_modules/pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
         "inherits": "^2.0.1"
       }
     },
+    "node_modules/rope-sequence": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
+      "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==",
+      "dev": true
+    },
     "node_modules/safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
       "dev": true
     },
+    "node_modules/sass": {
+      "version": "1.37.5",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.37.5.tgz",
+      "integrity": "sha512-Cx3ewxz9QB/ErnVIiWg2cH0kiYZ0FPvheDTVC6BsiEGBTZKKZJ1Gq5Kq6jy3PKtL6+EJ8NIoaBW/RSd2R6cZOA==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/sass/node_modules/anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/sass/node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sass/node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sass/node_modules/chokidar": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+      "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/sass/node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sass/node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/sass/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/sass/node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/sass/node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/sass/node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/sass/node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
     "node_modules/sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
         "browser-process-hrtime": "^1.0.0"
       }
     },
+    "node_modules/w3c-keyname": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz",
+      "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==",
+      "dev": true
+    },
     "node_modules/w3c-xmlserializer": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
       "dev": true,
       "peer": true
     },
+    "@toast-ui/editor": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@toast-ui/editor/-/editor-3.0.2.tgz",
+      "integrity": "sha512-auyfN5OzXZoR76FeR9kVKuZctEPj15i9PQOM4ebE1VOWyJ9VPXPwFYDZ1KQNWkxkMWWqw7qI28VDIUQhTvK3mg==",
+      "dev": true,
+      "requires": {
+        "prosemirror-commands": "^1.1.9",
+        "prosemirror-history": "^1.1.3",
+        "prosemirror-inputrules": "^1.1.3",
+        "prosemirror-keymap": "^1.1.4",
+        "prosemirror-model": "^1.14.1",
+        "prosemirror-state": "^1.3.4",
+        "prosemirror-view": "^1.18.7"
+      }
+    },
+    "@toast-ui/vue-editor": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@toast-ui/vue-editor/-/vue-editor-3.0.2.tgz",
+      "integrity": "sha512-16ODRlWGvVpAa8wgI6i93O3Op3kn5BCKDO8fVsQO1KLTgaBCwTYoCE9DWcwPMXgmzCAWEYypnphpXx1M86BJIg==",
+      "dev": true,
+      "requires": {
+        "@toast-ui/editor": "^3.0.2"
+      }
+    },
     "@types/q": {
       "version": "1.5.5",
       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz",
         "wcwidth": "^1.0.1"
       }
     },
+    "orderedmap": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.1.tgz",
+      "integrity": "sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ==",
+      "dev": true
+    },
     "os-browserify": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
       "integrity": "sha1-GN4vl+S/epVRrXURlCtUlverpmA=",
       "dev": true
     },
+    "picomatch": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+      "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+      "dev": true
+    },
     "pn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
       "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
+    "prosemirror-commands": {
+      "version": "1.1.10",
+      "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.1.10.tgz",
+      "integrity": "sha512-IWyBBXNAd44RM6NnBPljwq+/CM2oYCQJkF+YhKEAZNwzW0uFdGf4qComhjbKZzqFdu6Iub2ZhNsXgwPibA0lCQ==",
+      "dev": true,
+      "requires": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "prosemirror-history": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.1.3.tgz",
+      "integrity": "sha512-zGDotijea+vnfnyyUGyiy1wfOQhf0B/b6zYcCouBV8yo6JmrE9X23M5q7Nf/nATywEZbgRLG70R4DmfSTC+gfg==",
+      "dev": true,
+      "requires": {
+        "prosemirror-state": "^1.2.2",
+        "prosemirror-transform": "^1.0.0",
+        "rope-sequence": "^1.3.0"
+      }
+    },
+    "prosemirror-inputrules": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
+      "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
+      "dev": true,
+      "requires": {
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "prosemirror-keymap": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz",
+      "integrity": "sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==",
+      "dev": true,
+      "requires": {
+        "prosemirror-state": "^1.0.0",
+        "w3c-keyname": "^2.2.0"
+      }
+    },
+    "prosemirror-model": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.14.3.tgz",
+      "integrity": "sha512-yzZlBaSxfUPIIP6U5Edh5zKxJPZ5f7bwZRhiCuH3UYkWhj+P3d8swHsbuAMOu/iDatDc5J/Qs5Mb3++mZf+CvQ==",
+      "dev": true,
+      "requires": {
+        "orderedmap": "^1.1.0"
+      }
+    },
+    "prosemirror-state": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
+      "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
+      "dev": true,
+      "requires": {
+        "prosemirror-model": "^1.0.0",
+        "prosemirror-transform": "^1.0.0"
+      }
+    },
+    "prosemirror-transform": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.2.tgz",
+      "integrity": "sha512-/G6d/u9Mf6Bv3H1XR8VxhpjmUO75LYmnvj+s3ZfZpakU1hnQbsvCEybml1B3f2IWUAAQRFkbO1PnsbFhLZsYsw==",
+      "dev": true,
+      "requires": {
+        "prosemirror-model": "^1.0.0"
+      }
+    },
+    "prosemirror-view": {
+      "version": "1.18.11",
+      "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.18.11.tgz",
+      "integrity": "sha512-KXUM8UEV+IK4JYWHNyxkPGDGbxeTEUHQv3POApfyTRN5eMcPFbY4cB0mDJr0LPelVvYPghmZDOCqfCIm9mYHtQ==",
+      "dev": true,
+      "requires": {
+        "prosemirror-model": "^1.14.3",
+        "prosemirror-state": "^1.0.0",
+        "prosemirror-transform": "^1.1.0"
+      }
+    },
     "pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
         "inherits": "^2.0.1"
       }
     },
+    "rope-sequence": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
+      "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==",
+      "dev": true
+    },
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
       "dev": true
     },
+    "sass": {
+      "version": "1.37.5",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.37.5.tgz",
+      "integrity": "sha512-Cx3ewxz9QB/ErnVIiWg2cH0kiYZ0FPvheDTVC6BsiEGBTZKKZJ1Gq5Kq6jy3PKtL6+EJ8NIoaBW/RSd2R6cZOA==",
+      "dev": true,
+      "requires": {
+        "chokidar": ">=3.0.0 <4.0.0"
+      },
+      "dependencies": {
+        "anymatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+          "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+          "dev": true,
+          "requires": {
+            "normalize-path": "^3.0.0",
+            "picomatch": "^2.0.4"
+          }
+        },
+        "binary-extensions": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+          "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+          "dev": true
+        },
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+          "dev": true,
+          "requires": {
+            "fill-range": "^7.0.1"
+          }
+        },
+        "chokidar": {
+          "version": "3.5.2",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+          "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+          "dev": true,
+          "requires": {
+            "anymatch": "~3.1.2",
+            "braces": "~3.0.2",
+            "fsevents": "~2.3.2",
+            "glob-parent": "~5.1.2",
+            "is-binary-path": "~2.1.0",
+            "is-glob": "~4.0.1",
+            "normalize-path": "~3.0.0",
+            "readdirp": "~3.6.0"
+          }
+        },
+        "fill-range": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+          "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+          "dev": true,
+          "requires": {
+            "to-regex-range": "^5.0.1"
+          }
+        },
+        "fsevents": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+          "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+          "dev": true,
+          "optional": true
+        },
+        "glob-parent": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+          "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        },
+        "is-binary-path": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+          "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+          "dev": true,
+          "requires": {
+            "binary-extensions": "^2.0.0"
+          }
+        },
+        "is-number": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+          "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+          "dev": true
+        },
+        "readdirp": {
+          "version": "3.6.0",
+          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+          "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+          "dev": true,
+          "requires": {
+            "picomatch": "^2.2.1"
+          }
+        },
+        "to-regex-range": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+          "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+          "dev": true,
+          "requires": {
+            "is-number": "^7.0.0"
+          }
+        }
+      }
+    },
     "sax": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
         "browser-process-hrtime": "^1.0.0"
       }
     },
+    "w3c-keyname": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz",
+      "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==",
+      "dev": true
+    },
     "w3c-xmlserializer": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
index c2a3b3e9321c3f0972d48f8d430dda6b557159b7..0099782f1739eb4e789fbb536fdf1c01b58f11d5 100644 (file)
   "author": "Adam Dullage",
   "license": "MIT",
   "devDependencies": {
+    "@toast-ui/vue-editor": "^3.0.2",
     "@vue/component-compiler-utils": "^3.2.2",
     "axios": "^0.21.1",
     "bootstrap": "^5.0.2",
     "bootstrap-vue": "^2.21.2",
     "parcel-bundler": "^1.12.5",
+    "sass": "^1.37.5",
     "vue": "^2.6.14",
     "vue-hot-reload-api": "^2.3.4",
     "vue-template-compiler": "^2.6.14"
git clone https://git.99rst.org/PROJECT