@tenjuu99/blog 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/new CHANGED
@@ -23,7 +23,7 @@ echo '{
23
23
  "url_base": "http://localhost:8000",
24
24
  "src_dir": "src",
25
25
  "dist_dir": "dist",
26
- "distribute_raw": "image",
26
+ "distribute_raw": "image,js",
27
27
  "helper": "helper/index.js"
28
28
  }' > "$(pwd)/blog.json"
29
29
  echo "create blog.json"
package/lib/config.js CHANGED
@@ -18,13 +18,6 @@ try {
18
18
  config[item] = configOverride[item]
19
19
  }
20
20
  }
21
- const keys = Object.keys(process.env)
22
- for (const item in config) {
23
- const upper = item.toUpperCase()
24
- if (keys.includes(upper)) {
25
- config[item] = process.env[upper]
26
- }
27
- }
28
21
  } catch (e) {
29
22
  console.log(e)
30
23
  }
package/lib/distribute.js CHANGED
@@ -21,8 +21,11 @@ const distribute = async (data, srcDir, distDir) => {
21
21
  const promises = []
22
22
  const newIndex = []
23
23
  for (const name in data) {
24
+ if (!data[name].distribute) {
25
+ continue
26
+ }
24
27
  promises.push(renderPage(data[name]))
25
- newIndex.push({ name: data.name, url: data.url, __output: data.__output })
28
+ newIndex.push({ name: data[name].name, url: data[name].url, __output: data[name].__output })
26
29
  }
27
30
  const renderedString = await Promise.all(promises)
28
31
  for (const page of renderedString) {
@@ -50,7 +53,7 @@ const distribute = async (data, srcDir, distDir) => {
50
53
  fs.readFile(indexFile, 'utf8')
51
54
  .then(text => {
52
55
  const oldIndex = JSON.parse(text)
53
- deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
56
+ let deleted = oldIndex.filter(oi => !newIndex.map(ni => ni.__output).includes(oi.__output))
54
57
  fs.writeFile(indexFile, JSON.stringify(newIndex))
55
58
  if (deleted) {
56
59
  for (const obj of deleted) {
@@ -60,6 +63,7 @@ const distribute = async (data, srcDir, distDir) => {
60
63
  }
61
64
  })
62
65
  .catch(error => {
66
+ console.log(error)
63
67
  fs.writeFile(indexFile, JSON.stringify(newIndex))
64
68
  })
65
69
  }
package/lib/files.js CHANGED
@@ -21,7 +21,7 @@ const staticFile = (name) => {
21
21
  if (!loaded) {
22
22
  throw new Error('not initialized')
23
23
  }
24
- if (staticFilesContainer[name]) {
24
+ if (typeof staticFilesContainer[name] !== 'undefined') {
25
25
  return staticFilesContainer[name]
26
26
  }
27
27
  if (name.indexOf('template/') === 0) {
package/lib/filter.js CHANGED
@@ -37,6 +37,23 @@ const ifConditionEvaluator = (condition, variables) => {
37
37
  return left != right
38
38
  }
39
39
  } else {
40
+ const match = condition.match(/([\w]+)\((.*)\)/)
41
+ if (match) {
42
+ const func = match[1]
43
+ if (helper[func] instanceof Function) {
44
+ let args = match[2].trim()
45
+ if (args) {
46
+ args = args.split(',').map(arg => {
47
+ const match = arg.match(/^(['"])?\s*([\w]+)(["'])?$/)
48
+ if (match) {
49
+ return match[1] ? `${match[2]}` : variables[match[2]]
50
+ }
51
+ return arg
52
+ })
53
+ }
54
+ return helper[func].call(args)
55
+ }
56
+ }
40
57
  if (variables.hasOwnProperty(condition)) {
41
58
  return !!variables[condition]
42
59
  }
@@ -56,7 +73,7 @@ const replaceIfFilter = (text, variables) => {
56
73
  const target = item[0]
57
74
  const content = item.groups.content
58
75
  const elseContent = item.groups.elsecontent
59
- if (ifConditionEvaluator(item.groups.condition, variables)) {
76
+ if (ifConditionEvaluator(item.groups.condition.trim(), variables)) {
60
77
  text = text.replace(target, content)
61
78
  } else if (elseContent) {
62
79
  text = text.replace(target, elseContent)
package/lib/indexer.js CHANGED
@@ -33,7 +33,9 @@ const indexing = async () => {
33
33
  const files = await Promise.all(collect(pageDir))
34
34
  files.forEach((file) => {
35
35
  const pageData = makePageData(file[0], file[1])
36
- allData[pageData.name] = pageData
36
+ if (pageData.distribute) {
37
+ allData[pageData.name] = pageData
38
+ }
37
39
  })
38
40
  }
39
41
 
package/lib/pageData.js CHANGED
@@ -20,6 +20,7 @@ const parse = (content, name, ext) => {
20
20
  index: true,
21
21
  noindex: false,
22
22
  lang: 'ja',
23
+ distribute: true,
23
24
  site_name: config.site_name,
24
25
  url_base: config.url_base,
25
26
  markdown: markdownReplaced,
@@ -40,6 +41,11 @@ const parse = (content, name, ext) => {
40
41
  let value = line.slice(index + 1).trim()
41
42
  if (value === 'true' || value === 'false') {
42
43
  value = JSON.parse(value)
44
+ } else {
45
+ if (value.startsWith('config.')) {
46
+ const envName = value.split('.').pop()
47
+ eval(`value = config.${envName}`)
48
+ }
43
49
  }
44
50
  return [key, value]
45
51
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenjuu99/blog",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "blog template",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,6 +23,9 @@
23
23
  "chokidar": "^3.6.0",
24
24
  "marked": "^13.x"
25
25
  },
26
+ "devDependencies": {
27
+ "@tenjuu99/blog": "file:./"
28
+ },
26
29
  "type": "module",
27
30
  "engines": {
28
31
  "node": ">=21.7"
@@ -0,0 +1,137 @@
1
+ body {
2
+ background: #fafafa;
3
+ --container-max-width: 90%;
4
+ --container-min-width: 90%;
5
+ --container-width: 90%;
6
+ height: 100vh;
7
+ }
8
+ main {
9
+ height: 100%;
10
+ margin: 0 auto;
11
+ }
12
+ .textareaAndPreview {
13
+ display: flex;
14
+ margin: 10px 0;
15
+ border: 1px solid #666;
16
+ border-radius: 5px;
17
+ height: 100%;
18
+ }
19
+ .textareaAndPreview>* {
20
+ flex-basis: 50%;
21
+ }
22
+ .editor {
23
+ display: flex;
24
+ flex-direction: column;
25
+ padding: 15px 0;
26
+ justify-content:center;
27
+ height: 100%;
28
+ }
29
+ #editorTextArea {
30
+ resize: none;
31
+ background: #cccccc;
32
+ color: #333;
33
+ padding: 5px;
34
+ border-radius: 5px 0 0 5px;
35
+ border: 0;
36
+ transition: background 0.2s;
37
+ }
38
+ #editorTextArea:focus {
39
+ background: #eee;
40
+ transition: background 0.2s;
41
+ outline: none;
42
+ }
43
+ form select {
44
+ padding: 5px;
45
+ margin-top: 5px;
46
+ border-radius: 5px;
47
+ }
48
+ #previewContent {
49
+ flex-basis: 100%;
50
+ height: 100%;
51
+ min-height: 90%;
52
+ display: block;
53
+ width: 100%;
54
+ }
55
+ #previewContent iframe {
56
+ width: 100%;
57
+ height: 100%;
58
+ border: none;
59
+ border-radius: 0 5px 5px 0;
60
+ }
61
+ .sidebar {
62
+ display: flex;
63
+ justify-content: space-between;
64
+ width: 50vw;
65
+ overflow: hidden;
66
+ position: absolute;
67
+ background: rgba(104, 117, 154);
68
+ height: 100%;
69
+ padding: 0;
70
+ box-shadow: 3px 13px 13px rgba(50, 50, 50, 0.5);
71
+ left: 0;
72
+ transition: left 0.3s, width 0.3s;
73
+ overflow-y: hidden;
74
+ }
75
+ .sidebar.close {
76
+ left: -48vw;
77
+ transition: left 0.3s, width 0.3s;
78
+ }
79
+ @media screen and (min-width: 1100px) {
80
+ .sidebar {
81
+ width: 40vw;
82
+ }
83
+ .sidebar.close {
84
+ left: -39vw;
85
+ }
86
+ }
87
+ @media screen and (min-width: 1400px) {
88
+ .sidebar {
89
+ width: 30vw;
90
+ }
91
+ .sidebar.close {
92
+ left: -29vw;
93
+ }
94
+ }
95
+ @media screen and (max-width: 600px) {
96
+ .sidebar {
97
+ width: 80vw;
98
+ }
99
+ .sidebar.close {
100
+ left: -77vw;
101
+ }
102
+ }
103
+ .sidebar li {
104
+ list-style: none;
105
+ }
106
+ .sidebar a {
107
+ color: #fff;
108
+ }
109
+ .sidebar-toggle {
110
+ cursor: pointer;
111
+ width: 20px;
112
+ height: 100%;
113
+ position: relative;
114
+ }
115
+ .sidebar-toggle:hover:after {
116
+ position: absolute;
117
+ right: 0;
118
+ content: ' ';
119
+ height: 100%;
120
+ width: 20px;
121
+ background: rgba(255, 255,255, 0.3);
122
+ }
123
+ @media screen and (max-width: 600px) {
124
+ main {
125
+ width: 100%;
126
+ display: block;
127
+ }
128
+ .editor {
129
+ padding: 15px 0;
130
+ }
131
+ .preview {
132
+ display: none;
133
+ }
134
+ #editorTextArea {
135
+ flex-basis: 100%;
136
+ }
137
+ }
@@ -4,6 +4,9 @@
4
4
  --headline-3: 16px;
5
5
  --text-size: 16px;
6
6
  --text-size-small: 12px;
7
+ --container-max-width: 80%;
8
+ --container-min-width: 600px;
9
+ --container-width: 70vw;
7
10
  }
8
11
 
9
12
  body {
@@ -21,16 +24,16 @@ body {
21
24
  .container {
22
25
  margin: 0 auto;
23
26
  padding: 5px 30px;
24
- max-width: 80%;
25
- min-width: 600px;
26
- width: 70vw;
27
+ max-width: var(--container-max-width, 80%);
28
+ min-width: var(--container-min-width, 600px);
29
+ width: var(--container-width, 70vw);
27
30
  }
28
31
 
29
32
  @media screen and (max-width: 600px) {
30
33
  .container {
31
- max-width: 100%;
32
- width: 100%;
33
- min-width: 100%;
34
+ --container-max-width: 100%;
35
+ --container-min-width: 100%;
36
+ --container-width: 100%;
34
37
  padding: 5px 15px;
35
38
  }
36
39
  }
@@ -1,3 +1,9 @@
1
+ import { allData } from '@tenjuu99/blog/lib/indexer.js'
2
+
1
3
  export function additionalHelper() {
2
4
  return 'これは追加ヘルパーによって出力されているメッセージです。'
3
5
  }
6
+
7
+ export function isEditorEnabled() {
8
+ return allData.editor && allData.editor.distribute
9
+ }
@@ -0,0 +1,100 @@
1
+ const sleep = waitTime => new Promise( resolve => setTimeout(resolve, waitTime) );
2
+
3
+ const fetchData = (target) => {
4
+ return fetch(`/get_editor_target?md=${target}`)
5
+ .then(async res => {
6
+ const json = await res.json()
7
+ return json
8
+ })
9
+ }
10
+ const onloadFunction = async (e) => {
11
+ const form = document.querySelector('#editor')
12
+ const textarea = form.querySelector('#editorTextArea')
13
+ const select = form.querySelector('#selectDataFile')
14
+ const inputFileName = form.querySelector('#inputFileName')
15
+ const preview = document.querySelector('#previewContent')
16
+ const url = new URL(location)
17
+ const target = url.searchParams.get('md')
18
+ if (target) {
19
+ fetchData(target).then(json => {
20
+ textarea.value = json.content
21
+ select.value = json.filename
22
+ inputFileName.value = json.filename
23
+ inputFileName.setAttribute('disabled', true)
24
+ submit('/preview', form)
25
+ })
26
+ }
27
+ select.addEventListener('change', async (event) => {
28
+ if (select.value) {
29
+ const json = await fetchData(select.value)
30
+ textarea.value = json.content
31
+ inputFileName.value = json.filename
32
+ inputFileName.setAttribute('disabled', true)
33
+ url.searchParams.set('md', select.value)
34
+ submit('/preview', form)
35
+ } else {
36
+ inputFileName.value = ""
37
+ inputFileName.removeAttribute('disabled')
38
+ textarea.value = ''
39
+ url.searchParams.set('md', "")
40
+ const iframe = preview.querySelector('iframe')
41
+ if (iframe) {
42
+ iframe.srcdoc = ''
43
+ }
44
+ }
45
+ history.pushState({}, "", url)
46
+ })
47
+
48
+ const submit = (fetchUrl, form) => {
49
+ const formData = new FormData(form)
50
+ const obj = {}
51
+ formData.forEach((v, k) => {
52
+ obj[k] = v
53
+ })
54
+ fetch(fetchUrl, {
55
+ method: 'post',
56
+ body: JSON.stringify(obj)
57
+ }).then(async response => {
58
+ const json = await response.json()
59
+ if (!response.ok) {
60
+ alert(json.message)
61
+ return
62
+ }
63
+ if (json.href) {
64
+ await sleep(300)
65
+ location.href = json.href
66
+ }
67
+ if (json.preview) {
68
+ const iframe = document.createElement('iframe')
69
+ iframe.setAttribute('srcdoc', json.preview)
70
+ iframe.setAttribute('sandbox', '')
71
+ const old = preview.querySelector('iframe')
72
+ if (!old) {
73
+ preview.appendChild(iframe)
74
+ }
75
+ old.setAttribute('srcdoc', json.preview)
76
+ }
77
+ }).catch(e => {
78
+ console.log(e.message)
79
+ })
80
+ }
81
+ form.addEventListener('submit', (event) => {
82
+ event.preventDefault()
83
+ const fetchUrl = event.submitter.dataset.url
84
+ submit(fetchUrl, event.target)
85
+ })
86
+ }
87
+
88
+ const sidebarToggle = (e) => {
89
+ const sidebar = document.querySelector('.sidebar')
90
+ const toggle = sidebar.querySelector('.sidebar-toggle')
91
+ toggle.addEventListener('click', (e) => {
92
+ e.preventDefault()
93
+ sidebar.classList.toggle('close')
94
+ })
95
+ }
96
+ document.addEventListener('DOMContentLoaded', (event) => {
97
+ console.log(event)
98
+ onloadFunction(event)
99
+ sidebarToggle(event)
100
+ })
@@ -0,0 +1,5 @@
1
+ ---
2
+ template: editor.html
3
+ index: false
4
+ distribute: config.editor_enable
5
+ ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  title: 投稿一覧
3
+ index: false
3
4
  ---
4
5
  <script type="ssg">
5
6
  variables.postPages = helper.readIndex().filter(v => v.url.indexOf('/post/') === 0)
@@ -1,11 +1,8 @@
1
1
  import { IncomingMessage, ServerResponse } from 'http'
2
2
  import fs from 'node:fs/promises'
3
3
  import { styleText } from 'node:util'
4
-
5
- const rootDir = process.cwd()
6
- const config = (await import(rootDir + '/lib/config.js')).default
7
- const dir = (await import(rootDir + '/lib/dir.js'))
8
- const helper = (await import(rootDir + '/lib/helper.js'))
4
+ import config from '@tenjuu99/blog/lib/config.js'
5
+ import { pageDir } from '@tenjuu99/blog/lib/dir.js'
9
6
 
10
7
  export const path = '/editor'
11
8
 
@@ -27,7 +24,7 @@ export const post = async (req, res) => {
27
24
  }))
28
25
  return
29
26
  }
30
- await fs.writeFile(`${dir.pageDir}/${file}`, json.content)
27
+ await fs.writeFile(`${pageDir}/${file}`, json.content)
31
28
  console.log(styleText('blue', '[editor/post] finished'))
32
29
 
33
30
  const href = file.split('.')[0]
@@ -1,9 +1,7 @@
1
1
  import { IncomingMessage, ServerResponse } from 'http'
2
2
  import fs from 'node:fs'
3
-
4
- const rootDir = process.cwd()
5
- const config = (await import(rootDir + '/lib/config.js')).default
6
- const pageDir = (await import(rootDir + '/lib/dir.js')).pageDir
3
+ import config from '@tenjuu99/blog/lib/config.js'
4
+ import { pageDir } from '@tenjuu99/blog/lib/dir.js'
7
5
 
8
6
  export const path = '/get_editor_target'
9
7
 
@@ -1,9 +1,7 @@
1
1
  import { IncomingMessage, ServerResponse } from 'http'
2
2
  import { styleText } from 'node:util'
3
-
4
- const rootDir = process.cwd()
5
- const render = (await import(rootDir + '/lib/render.js')).default
6
- const makePageData = (await import(rootDir + '/lib/pageData.js')).default
3
+ import render from '@tenjuu99/blog/lib/render.js'
4
+ import makePageData from '@tenjuu99/blog/lib/pageData.js'
7
5
 
8
6
  export const path = '/preview'
9
7
 
@@ -46,7 +46,7 @@
46
46
 
47
47
  {include('template/prevNext.html')}
48
48
  </main>
49
- {if editor}
49
+ {if isEditorEnabled() }
50
50
  <div class="container">
51
51
  <a href="/editor?md={{name}}.{{__filetype}}">編集する</a>
52
52
  </div>
@@ -5,175 +5,27 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>{{SITE_NAME}}</title>
7
7
  {include('template/css.html')}
8
- <style>
9
- body {
10
- background: #fafafa;
11
- }
12
- main {
13
- width: 90%;
14
- padding: 15px;
15
- height: 100%;
16
- margin: 0 auto;
17
- }
18
- .textareaAndPreview {
19
- display: flex;
20
- margin: 10px 0;
21
- border: 1px solid #666;
22
- border-radius: 5px;
23
- }
24
- .textareaAndPreview>* {
25
- flex-basis: 50%;
26
- height: 80vh;
27
- }
28
- .editor {
29
- display: flex;
30
- flex-direction: column;
31
- padding: 15px 0;
32
- justify-content:center;
33
- }
34
- #editorTextArea {
35
- resize: none;
36
- background: #cccccc;
37
- color: #333;
38
- padding: 5px;
39
- border-radius: 5px 0 0 5px;
40
- border: 0;
41
- }
42
- form select {
43
- padding: 5px;
44
- margin-top: 5px;
45
- border-radius: 5px;
46
- }
47
- #previewContent {
48
- flex-basis: 100%;
49
- height: 100%;
50
- min-height: 90%;
51
- display: block;
52
- width: 100%;
53
- }
54
- #previewContent iframe {
55
- width: 100%;
56
- height: 100%;
57
- border: none;
58
- border-radius: 0 5px 5px 0;
59
- pointer-events: none;
60
- }
61
- @media screen and (max-width: 600px) {
62
- main {
63
- width: 100%;
64
- display: block;
65
- }
66
- .editor {
67
- padding: 15px 0;
68
- }
69
- .preview {
70
- display: none;
71
- }
72
- #editorTextArea {
73
- flex-basis: 100%;
74
- }
75
- }
76
- </style>
8
+ <link rel="stylesheet" href="${/css/editor.css<<editor.css}">
9
+ <script src="/js/editor.js" defer></script>
77
10
 
78
- <link href="https://cdn.jsdelivr.net/npm/prismjs@v1.x/themes/prism.css" rel="stylesheet" />
79
-
80
- <script src="https://cdn.jsdelivr.net/npm/prismjs@v1.x/components/prism-core.min.js"></script>
81
- <script src="https://cdn.jsdelivr.net/npm/prismjs@v1.x/plugins/autoloader/prism-autoloader.min.js"></script>
82
11
  </head>
83
12
  <body>
84
- <script>
85
- const sleep = waitTime => new Promise( resolve => setTimeout(resolve, waitTime) );
86
-
87
- const fetchData = (target) => {
88
- return fetch(`/get_editor_target?md=${target}`)
89
- .then(async res => {
90
- const json = await res.json()
91
- return json
92
- })
93
- }
94
- const onloadFunction = async (e) => {
95
- const form = document.querySelector('#editor')
96
- const textarea = form.querySelector('#editorTextArea')
97
- const select = form.querySelector('#selectDataFile')
98
- const inputFileName = form.querySelector('#inputFileName')
99
- const preview = document.querySelector('#previewContent')
100
- const url = new URL(location)
101
- const target = url.searchParams.get('md')
102
- if (target) {
103
- fetchData(target).then(json => {
104
- textarea.value = json.content
105
- select.value = json.filename
106
- inputFileName.value = json.filename
107
- inputFileName.setAttribute('disabled', true)
108
- submit('/preview', form)
109
- })
110
- }
111
- select.addEventListener('change', async (event) => {
112
- if (select.value) {
113
- const json = await fetchData(select.value)
114
- textarea.value = json.content
115
- inputFileName.value = json.filename
116
- inputFileName.setAttribute('disabled', true)
117
- url.searchParams.set('md', select.value)
118
- submit('/preview', form)
119
- } else {
120
- inputFileName.value = ""
121
- inputFileName.removeAttribute('disabled')
122
- textarea.value = ''
123
- url.searchParams.set('md', "")
124
- const iframe = preview.querySelector('iframe')
125
- if (iframe) {
126
- iframe.srcdoc = ''
127
- }
128
- }
129
- history.pushState({}, "", url)
130
- })
131
-
132
- const submit = (fetchUrl, form) => {
133
- const formData = new FormData(form)
134
- const obj = {}
135
- formData.forEach((v, k) => {
136
- obj[k] = v
137
- })
138
- fetch(fetchUrl, {
139
- method: 'post',
140
- body: JSON.stringify(obj)
141
- }).then(async response => {
142
- const json = await response.json()
143
- if (!response.ok) {
144
- alert(json.message)
145
- return
146
- }
147
- if (json.href) {
148
- await sleep(300)
149
- location.href = json.href
150
- }
151
- if (json.preview) {
152
- const iframe = document.createElement('iframe')
153
- iframe.setAttribute('srcdoc', json.preview)
154
- iframe.setAttribute('sandbox', '')
155
- const old = preview.querySelector('iframe')
156
- if (!old) {
157
- preview.appendChild(iframe)
158
- }
159
- old.setAttribute('srcdoc', json.preview)
160
- }
161
- }).catch(e => {
162
- console.log(e.message)
163
- })
164
- }
165
- form.addEventListener('submit', (event) => {
166
- event.preventDefault()
167
- const fetchUrl = event.submitter.dataset.url
168
- submit(fetchUrl, event.target)
169
- })
170
- }
171
-
172
- document.addEventListener('DOMContentLoaded', (event) => {
173
- onloadFunction(event)
174
- })
175
- </script>
176
- <main>
13
+ <header>
14
+ <p class="container">
15
+ <a href="/">{{SITE_NAME}}/Editor</a>
16
+ </p>
17
+ </header>
18
+ <div class="sidebar close">
19
+ <ul>
20
+ <script type="ssg">
21
+ return helper.readIndex().map(p => {
22
+ return `<li><a href="/editor?md=${p.name}.${p.__filetype}">${p.url}.${p.__filetype}</a></li>`
23
+ }).join("\n")
24
+ </script>
25
+ </ul>
26
+ <div class="sidebar-toggle">&nbsp;</div>
27
+ </div>
28
+ <main class="container">
177
29
  <form action="/editor" class="editor" method="post" id="editor">
178
30
  <input id="inputFileName" name="inputFileName" type="text" value="" placeholder="sample.md">
179
31
  <select id="selectDataFile" name="selectDataFile">
@@ -197,7 +49,7 @@
197
49
  <input type="hidden" name="token" value="{{TOKEN}}">
198
50
  <div>
199
51
  <input type="submit" value="preview" data-url="/preview">
200
- <input type="submit" value="編集" data-url="/editor">
52
+ <input type="submit" value="save" data-url="/editor">
201
53
  <a href="/">戻る</a>
202
54
  </div>
203
55
  </form>
@@ -1,3 +0,0 @@
1
- ---
2
- template: editor.html
3
- ---