@tenjuu99/blog 0.4.1 → 0.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenjuu99/blog",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "blog template",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -8,7 +8,8 @@
8
8
  "server": "node bin/server",
9
9
  "generate": "node bin/generate",
10
10
  "test": "node --test --test-concurrency=1 tests/**/*.test.js",
11
- "test:watch": "node --test --watch tests/**/*.test.js"
11
+ "test:watch": "node --test --watch tests/**/*.test.js",
12
+ "test:acceptance": "npx playwright test --project=chromium"
12
13
  },
13
14
  "bin": {
14
15
  "generate": "bin/generate",
@@ -1,3 +1,13 @@
1
+ :root {
2
+ --sidebar-width: 300px;
3
+ --sidebar-close-width: 20px;
4
+ --sidebar-transition-speed: 0.2s;
5
+ --header-height: 30px;
6
+ --footer-height: 20px;
7
+ --light-green: #79afb4;
8
+ --deep-blue: #313d64;
9
+ --hover-bg: rgba(104, 117, 154, .3);
10
+ }
1
11
  body {
2
12
  background: #fafafa;
3
13
  height: 100vh;
@@ -13,10 +23,6 @@ header {
13
23
  }
14
24
  main {
15
25
  height: 100%;
16
- /*margin: 0 auto;*/
17
- display: flex;
18
- flex-direction: row;
19
- justify-content: flex-start;
20
26
  }
21
27
  main.container {
22
28
  padding: 0;
@@ -25,7 +31,6 @@ main.container {
25
31
  display: flex;
26
32
  margin: 0;
27
33
  border: 1px solid #666;
28
- border-radius: 5px;
29
34
  height: 100%;
30
35
  }
31
36
  .textareaAndPreview>* {
@@ -34,37 +39,52 @@ main.container {
34
39
  .editor {
35
40
  display: flex;
36
41
  flex-direction: column;
37
- padding: 5px;
38
42
  justify-content:center;
39
- height: 100%;
43
+ height: calc(100% - var(--footer-height));
40
44
  /* width: 100%; */
41
45
  margin: 0;
42
46
  position: relative;
43
- width: 100%;
47
+ width: calc(100% - var(--sidebar-close-width));
44
48
  left: 0;
45
49
  flex-basis: 100%;
50
+ transition: width var(--sidebar-transition-speed), left var(--sidebar-transition-speed);
46
51
  }
47
52
  .sidebar-close .editor {
48
- /*flex-basis: 90%;*/
49
- margin-left: 20px;
53
+ margin-left: var(--sidebar-close-width);
54
+ }
55
+ main:not(.sidebar-close) .editor {
56
+ width: calc(100% - var(--sidebar-width));
57
+ left: var(--sidebar-width);
58
+ transition: width var(--sidebar-transition-speed), left var(--sidebar-transition-speed);
50
59
  }
51
60
  .editor-options {
52
61
  display: flex;
53
62
  justify-content: space-between;
54
63
  align-items: center;
55
64
  gap: 8px;
56
- margin-bottom: 5px;
65
+ height: var(--header-height);
57
66
  }
58
67
  .editor-options-left {
59
68
  display: flex;
60
69
  align-items: center;
61
- gap: 8px;
62
70
  }
63
71
  .editor-options-right {
64
72
  display: flex;
65
73
  align-items: center;
66
- gap: 8px;
67
74
  }
75
+ #publishBtn {
76
+ height: var(--header-height);
77
+ width: 100px;
78
+ background: var(--deep-blue);
79
+ color: #ddd;
80
+ border-radius: 0;
81
+ border: none;
82
+ cursor: pointer;
83
+ }
84
+ #publishBtn:hover {
85
+ background: var(--light-green);
86
+ }
87
+
68
88
  #newFileDialog {
69
89
  border: 1px solid #ccc;
70
90
  border-radius: 8px;
@@ -93,14 +113,17 @@ main.container {
93
113
  background: #cccccc;
94
114
  color: #333;
95
115
  padding: 5px;
96
- border-radius: 5px 0 0 5px;
97
116
  border: 0;
98
117
  transition: background 0.2s;
118
+ box-shadow: inset -0.1em 0em 0.3em 0 rgba(0,0,0, 0.5);
119
+ font-weight: 500;
120
+ color: rgba(100,100,100,0.7);
99
121
  }
100
122
  #editorTextArea:focus {
101
123
  background: #eee;
102
124
  transition: background 0.2s;
103
125
  outline: none;
126
+ color: rgba(100,100,100,1);
104
127
  }
105
128
  form select {
106
129
  padding: 5px;
@@ -117,44 +140,127 @@ form select {
117
140
  width: 100%;
118
141
  height: 100%;
119
142
  border: none;
120
- border-radius: 0 5px 5px 0;
121
143
  }
122
144
 
123
145
  .sidebar {
124
- --sidebar-bg: #313d64;
125
- --sidebar-dir: #79afb4;
146
+ --sidebar-bg: var(--deep-blue);
147
+ --sidebar-dir: var(--light-green);
126
148
  --sidebar-file: #cbcbcb;
127
149
  --sidebar-active-file-fg: #333;
128
150
  --sidebar-active-file-bg: rgba(165,218,246,0.8);
129
151
  --sidebar-modified-fg: #ff7777;
130
- --sidebar-hover-bg: rgba(104, 117, 154, .3);
131
- display: flex;
132
- justify-content: space-between;
152
+ --sidebar-hover-bg: var(--hover-bg);
133
153
  width: 300px;
134
154
  overflow: scroll;
135
155
  left: 0;
136
156
  background: var(--sidebar-bg);
137
157
  height: 100%;
138
158
  padding: 0;
139
- flex-basis: 300px;
140
- position: relative;
159
+ width: var(--sidebar-width);
160
+ position: fixed;
141
161
  z-index: 3;
142
162
  font-size: 0.85em;
163
+ transition: left var(--sidebar-transition-speed);
164
+ display: flex;
165
+ justify-content: flex-start;
166
+ flex-direction: column;
167
+ }
168
+ .sidebar-tabs {
169
+ display: flex;
170
+ height: var(--header-height);
171
+ background: #2a3557;
172
+ flex-shrink: 0;
173
+ }
174
+ .sidebar-tab {
175
+ flex: 1;
176
+ border: none;
177
+ background: transparent;
178
+ color: rgba(203, 203, 203, 0.5);
179
+ cursor: pointer;
180
+ font-size: 0.8em;
181
+ border-bottom: 2px solid transparent;
182
+ transition: color 0.15s, border-color 0.15s;
183
+ padding: 0 8px;
184
+ }
185
+ .sidebar-tab.active {
186
+ color: #cbcbcb;
187
+ border-bottom-color: var(--light-green);
188
+ }
189
+ .sidebar-tab:hover:not(.active) {
190
+ color: rgba(203, 203, 203, 0.8);
191
+ background: rgba(255, 255, 255, 0.05);
192
+ }
193
+ .sidebar-tab-content {
194
+ display: none;
195
+ flex-direction: column;
196
+ flex: 1;
197
+ overflow: auto;
198
+ min-height: 0;
199
+ }
200
+ .sidebar-tab-content.active {
201
+ display: flex;
202
+ }
203
+ .sidebar-new-file {
204
+ display: flex;
205
+ flex-direction: column;
206
+ gap: 12px;
207
+ padding: 16px 12px;
208
+ color: var(--sidebar-file);
209
+ font-size: 0.9em;
210
+ }
211
+ .sidebar-new-file label {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: 4px;
215
+ }
216
+ .sidebar-new-file input[type="text"],
217
+ .sidebar-new-file select {
218
+ background: rgba(255, 255, 255, 0.1);
219
+ border: 1px solid rgba(255, 255, 255, 0.2);
220
+ border-radius: 4px;
221
+ color: var(--sidebar-file);
222
+ padding: 5px 8px;
223
+ font-size: 0.95em;
224
+ }
225
+ .sidebar-new-file input[type="text"]:focus,
226
+ .sidebar-new-file select:focus {
227
+ outline: none;
228
+ border-color: var(--sidebar-dir);
229
+ }
230
+ .sidebar-new-file select option {
231
+ background: var(--sidebar-bg);
232
+ }
233
+ .sidebar-new-file #newFileError {
234
+ color: var(--sidebar-modified-fg);
235
+ font-size: 0.9em;
236
+ min-height: 1.2em;
237
+ }
238
+ .sidebar-new-file button {
239
+ background: var(--sidebar-dir);
240
+ border: none;
241
+ border-radius: 4px;
242
+ color: #1a2540;
243
+ cursor: pointer;
244
+ font-weight: bold;
245
+ padding: 6px 12px;
246
+ align-self: flex-start;
247
+ }
248
+ .sidebar-new-file button:hover {
249
+ filter: brightness(1.1);
143
250
  }
144
251
  .sidebar-close .sidebar {
145
- left: -290px;
146
- flex-basis: 0;
252
+ left: calc(0px - var(--sidebar-width));
147
253
  }
148
254
  .sidebar ul {
149
255
  padding: 0;
150
256
  margin: 0;
151
257
  }
152
- .sidebar > ul {
258
+ .sidebar-files > ul {
153
259
  padding: 0 0 0 15px;
154
260
  }
155
261
  .sidebar-close .sidebar ul {
156
262
  position: absolute;
157
- left: -290px;
263
+ left: calc(10px - var(--sidebar-width));
158
264
  transition: left 0.3s;
159
265
  }
160
266
  .sidebar li {
@@ -210,10 +316,10 @@ form select {
210
316
  }
211
317
  .sidebar-toggle {
212
318
  cursor: pointer;
213
- width: 20px;
319
+ width: var(--sidebar-close-width);
214
320
  height: 100%;
215
321
  position: absolute;
216
- left: 0;
322
+ right: 0;
217
323
  }
218
324
  .sidebar-close .sidebar-toggle {
219
325
  position: fixed;
@@ -225,24 +331,35 @@ form select {
225
331
  right: 0;
226
332
  content: '';
227
333
  height: 100%;
228
- width: 20px;
334
+ width: var(--sidebar-close-width);
229
335
  background: rgba(255, 255,255, 0.3);
230
336
  }
337
+ .footer {
338
+ height: var(--footer-height);
339
+ background: #333;
340
+ padding-left: var(--sidebar-width);
341
+ color: white;
342
+ transition: padding-left var(--sidebar-transition-speed);
343
+ }
344
+ .sidebar-close .footer {
345
+ padding-left: var(--sidebar-close-width);
346
+ transition: padding-left var(--sidebar-transition-speed);
347
+ }
231
348
  @media screen and (max-width: 600px) {
349
+ :root {
350
+ --sidebar-sp-transition: transform var(--sidebar-transition-speed), background-color var(--sidebar-transition-speed);
351
+ --header-height: 50px;
352
+ }
232
353
  main {
233
354
  width: 100%;
234
355
  display: block;
235
356
  }
236
357
  main.container {
237
- padding: 0 5px;
358
+ padding: 0;
238
359
  }
239
- .sidebar {
240
- position: fixed;
241
- width: 80vw;
242
- z-index: 3;
243
- right: 0;
244
- left: unset;
245
- transition: right .3s, width .3s;
360
+ main:not(.sidebar-close) .editor {
361
+ width: 100%;
362
+ left: 0;
246
363
  }
247
364
  .sidebar-close .sidebar {
248
365
  width: 0;
@@ -251,24 +368,43 @@ form select {
251
368
  left: unset;
252
369
  transition: right .3s, width .3s;
253
370
  }
371
+ .sidebar-close .editor {
372
+ margin-left: 0;
373
+ margin: 0;
374
+ }
254
375
  .editor {
255
- padding: 15px 0;
376
+ width: 100%;
377
+ padding: 0;
378
+ }
379
+ .editor-options {
380
+ margin-right: 50px;
381
+ }
382
+ .editor-options * {
383
+ display: flex;
384
+ flex-direction: column;
256
385
  }
257
386
  .preview {
258
387
  display: none;
259
388
  }
260
389
  #editorTextArea {
261
390
  flex-basis: 100%;
391
+ box-shadow: none;
262
392
  }
263
393
  .sidebar-toggle {
264
394
  display: none;
265
395
  }
266
- .sidebar {
267
- flex-basis: 80%;
268
- }
269
396
  .sidebar.close {
270
397
  flex-basis: 5%;
271
398
  }
399
+ .sidebar {
400
+ position: fixed;
401
+ flex-basis: 80%;
402
+ width: 80vw;
403
+ z-index: 3;
404
+ right: 0;
405
+ left: unset;
406
+ transition: right .3s, width .3s;
407
+ }
272
408
  }
273
409
  /**
274
410
  * hamburger
@@ -277,17 +413,6 @@ form select {
277
413
  display: none;
278
414
  }
279
415
  @media screen and (max-width: 600px) {
280
- .sidebar-close .editor {
281
- margin-left: 0;
282
- margin: 0;
283
- }
284
- .editor-options {
285
- margin-right: 50px;
286
- }
287
- .editor-options * {
288
- display: flex;
289
- flex-direction: column;
290
- }
291
416
  .hamburger-menu {
292
417
  display: block;
293
418
  }
@@ -301,7 +426,6 @@ form select {
301
426
  justify-content: center;
302
427
  align-items: center;
303
428
  z-index: 90;
304
- /* background-color: #3584bb; */
305
429
  }
306
430
  .menu-btn span,
307
431
  .menu-btn span:before,
@@ -313,7 +437,7 @@ form select {
313
437
  border-radius: 3px;
314
438
  background-color: #666666;
315
439
  position: absolute;
316
- transition: transform 0.3s, background-color 0.3s;
440
+ transition: var(--sidebar-sp-transition);
317
441
  }
318
442
  .menu-btn span:before {
319
443
  bottom: 8px;
@@ -321,18 +445,18 @@ form select {
321
445
  .menu-btn span:after {
322
446
  top: 8px;
323
447
  }
324
- #menu-btn-check:checked ~ .menu-btn span {
448
+ #menu-btn-check:not(:checked) ~ .menu-btn span {
325
449
  background-color: rgba(255, 255, 255, 0);/*メニューオープン時は真ん中の線を透明にする*/
326
450
  }
327
- #menu-btn-check:checked ~ .menu-btn span::before {
451
+ #menu-btn-check:not(:checked) ~ .menu-btn span::before {
328
452
  bottom: 0;
329
453
  transform: rotate(45deg);
330
- transition: transform 0.3s, background-color 0.3s;
454
+ transition: var(--sidebar-sp-transition);
331
455
  }
332
- #menu-btn-check:checked ~ .menu-btn span::after {
456
+ #menu-btn-check:not(:checked) ~ .menu-btn span::after {
333
457
  top: 0;
334
458
  transform: rotate(-45deg);
335
- transition: transform 0.3s, background-color 0.3s;
459
+ transition: var(--sidebar-sp-transition);
336
460
  }
337
461
  #menu-btn-check {
338
462
  display: none;
@@ -173,31 +173,13 @@ const onloadFunction = async (e) => {
173
173
  initDropReceiver(textarea, () => inputFileName.value, () => submit('/preview', form), () => debouncedUpdate.cancel())
174
174
 
175
175
  // @vocab: 新規作成UI
176
- const newFileBtn = document.querySelector('#newFileBtn')
177
- const newFileDialog = document.querySelector('#newFileDialog')
178
176
  const newFileNameInput = document.querySelector('#newFileName')
179
177
  const newFileTemplateSelect = document.querySelector('#newFileTemplate')
180
178
  const newFileError = document.querySelector('#newFileError')
181
179
  const confirmNewFile = document.querySelector('#confirmNewFile')
182
- const cancelNewFile = document.querySelector('#cancelNewFile')
183
180
 
184
181
  const refreshSidebar = () => initSidebarContent(inputFileName.value)
185
182
 
186
- newFileBtn.addEventListener('click', () => {
187
- newFileNameInput.value = ''
188
- newFileError.textContent = ''
189
- // テンプレート選択肢を再構築して auto-select をリセット
190
- newFileTemplateSelect.innerHTML = '<option value="">テンプレートなし</option>'
191
- for (const tmpl of _frontmatterTemplates) {
192
- const option = document.createElement('option')
193
- option.value = tmpl.path_prefix
194
- option.textContent = `テンプレート: ${tmpl.path_prefix}`
195
- newFileTemplateSelect.appendChild(option)
196
- }
197
- newFileTemplateSelect.value = ''
198
- newFileDialog.showModal()
199
- })
200
-
201
183
  // ファイル名入力に応じてテンプレートを auto-select
202
184
  newFileNameInput.addEventListener('input', () => {
203
185
  const filename = newFileNameInput.value
@@ -213,7 +195,7 @@ const onloadFunction = async (e) => {
213
195
  }
214
196
  })
215
197
 
216
- // F-02: 重複チェック付き作成。エラー時はダイアログを閉じない
198
+ // F-02: 重複チェック付き作成。エラー時はタブを閉じない
217
199
  confirmNewFile.addEventListener('click', async () => {
218
200
  const filename = newFileNameInput.value.trim()
219
201
  if (!filename) return
@@ -239,7 +221,7 @@ const onloadFunction = async (e) => {
239
221
  return
240
222
  }
241
223
 
242
- newFileDialog.close()
224
+ switchSidebarTab('files')
243
225
  textarea.value = content
244
226
  setCurrentFile(filename)
245
227
  url.searchParams.set('md', filename)
@@ -249,8 +231,6 @@ const onloadFunction = async (e) => {
249
231
  refreshSidebar()
250
232
  })
251
233
 
252
- cancelNewFile.addEventListener('click', () => newFileDialog.close())
253
-
254
234
  // インプレースファイル読み込み
255
235
  // サイドバーリンクのクリックやブラウザ履歴移動で呼ばれる。
256
236
  // turbolink によるページ全体の差し替えではなく、エディタ状態をその場で更新することでちらつきを防ぐ。
@@ -367,16 +347,52 @@ const initSidebarContent = async (activeFile) => {
367
347
  const res = await fetch('/get_sidebar')
368
348
  if (!res.ok) return
369
349
  const { html } = await res.json()
370
- const sidebar = document.querySelector('.sidebar')
371
- const toggle = sidebar.querySelector('.sidebar-toggle')
350
+ const sidebar = document.querySelector('.sidebar-files')
372
351
  sidebar.innerHTML = html
373
- sidebar.appendChild(toggle)
374
352
  initSidebarTree(activeFile)
375
353
  } catch (e) {
376
354
  console.log('[sidebar] init failed', e)
377
355
  }
378
356
  }
379
357
 
358
+ // @vocab: サイドバータブ
359
+ let switchSidebarTab = () => {}
360
+ const initSidebarTabs = () => {
361
+ const tabs = document.querySelectorAll('.sidebar-tab')
362
+ const contents = document.querySelectorAll('.sidebar-tab-content')
363
+ switchSidebarTab = (tabName) => {
364
+ tabs.forEach(t => {
365
+ const selected = t.dataset.tab === tabName
366
+ t.classList.toggle('active', selected)
367
+ t.setAttribute('aria-selected', String(selected))
368
+ t.tabIndex = selected ? 0 : -1
369
+ })
370
+ contents.forEach(c => {
371
+ const selected = c.dataset.tab === tabName
372
+ c.classList.toggle('active', selected)
373
+ c.toggleAttribute('hidden', !selected)
374
+ })
375
+ }
376
+ tabs.forEach(tab => {
377
+ tab.addEventListener('click', () => {
378
+ switchSidebarTab(tab.dataset.tab)
379
+ if (tab.dataset.tab === 'new-file') {
380
+ const select = document.querySelector('#newFileTemplate')
381
+ select.innerHTML = '<option value="">テンプレートなし</option>'
382
+ for (const tmpl of _frontmatterTemplates) {
383
+ const option = document.createElement('option')
384
+ option.value = tmpl.path_prefix
385
+ option.textContent = `テンプレート: ${tmpl.path_prefix}`
386
+ select.appendChild(option)
387
+ }
388
+ document.querySelector('#newFileName').value = ''
389
+ document.querySelector('#newFileError').textContent = ''
390
+ document.querySelector('#newFileName').focus()
391
+ }
392
+ })
393
+ })
394
+ }
395
+
380
396
  // @vocab: サイドバー
381
397
  // @test: tests/editor/editor-sidebar.test.js
382
398
  const sidebarToggle = (e) => {
@@ -441,6 +457,7 @@ const initDropReceiver = (textarea, getMdFile, onUpdate, cancelPendingDebounce)
441
457
  document.addEventListener('DOMContentLoaded', async (event) => {
442
458
  const url = new URL(location)
443
459
  const activeFile = url.searchParams.get('md') || ''
460
+ initSidebarTabs()
444
461
  await initSidebarContent(activeFile)
445
462
  await initFrontmatterTemplate()
446
463
  onloadFunction(event)
@@ -4,20 +4,47 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>{{SITE_NAME}}</title>
7
- <link rel="stylesheet" href="${/css/editor.css<<reset.css,editor.css}">
7
+ <style>
8
+ *,*::before,*::after{box-sizing:border-box}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role="list"],ol[role="list"]{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}
9
+ </style>
10
+ <link rel="stylesheet" href="${/css/editor.css<<editor.css}">
8
11
  <script type="module" src="/js/editor.js"></script>
9
12
 
10
13
  </head>
11
14
  <body>
12
15
  <main class="container">
16
+ <!-- layout shift 対策 -->
17
+ <script>if(localStorage.getItem('sidebar-is-open')!=='true'){document.currentScript.closest('main').classList.add('sidebar-close')}</script>
13
18
  <div class="sidebar">
19
+ <div class="sidebar-tabs" role="tablist">
20
+ <button class="sidebar-tab active" data-tab="files" role="tab" id="sidebar-tab-files" aria-selected="true" aria-controls="sidebar-tabpanel-files" tabindex="0">Files</button>
21
+ <button class="sidebar-tab" data-tab="new-file" role="tab" id="sidebar-tab-new-file" aria-selected="false" aria-controls="sidebar-tabpanel-new-file" tabindex="-1">新規作成</button>
22
+ </div>
23
+ <div class="sidebar-tab-content active" data-tab="files" role="tabpanel" id="sidebar-tabpanel-files" aria-labelledby="sidebar-tab-files">
24
+ <div class="sidebar-files"></div>
25
+ </div>
26
+ <div class="sidebar-tab-content" data-tab="new-file" role="tabpanel" id="sidebar-tabpanel-new-file" aria-labelledby="sidebar-tab-new-file" hidden>
27
+ <div class="sidebar-new-file">
28
+ <label>
29
+ ファイル名
30
+ <input type="text" id="newFileName" placeholder="path/to/file.md" autocomplete="off">
31
+ </label>
32
+ <label>
33
+ テンプレート
34
+ <select id="newFileTemplate">
35
+ <option value="">テンプレートなし</option>
36
+ </select>
37
+ </label>
38
+ <p id="newFileError" role="alert" aria-live="assertive"></p>
39
+ <button type="button" id="confirmNewFile">作成</button>
40
+ </div>
41
+ </div>
14
42
  <div class="sidebar-toggle">&nbsp;</div>
15
43
  </div>
16
44
  <form action="/editor" class="editor" method="post" id="editor">
17
45
  <div class="editor-options">
18
46
  <div class="editor-options-left">
19
47
  <span id="fileStatus"><span id="currentFileName"></span><span id="publicationStatus"></span></span>
20
- <button type="button" id="newFileBtn">新規作成</button>
21
48
  <input type="hidden" id="inputFileName" name="inputFileName" value="">
22
49
  </div>
23
50
  <div class="editor-options-right">
@@ -35,26 +62,7 @@
35
62
  </div>
36
63
  <input type="hidden" name="token" value="{{TOKEN}}">
37
64
  </form>
38
- <dialog id="newFileDialog">
39
- <form method="dialog">
40
- <h3>新規ファイルを作成</h3>
41
- <label>
42
- ファイル名(例: book/my-book.md)
43
- <input type="text" id="newFileName" placeholder="path/to/file.md" autocomplete="off">
44
- </label>
45
- <label>
46
- テンプレート
47
- <select id="newFileTemplate">
48
- <option value="">テンプレートなし</option>
49
- </select>
50
- </label>
51
- <p id="newFileError" role="alert" aria-live="assertive"></p>
52
- <div>
53
- <button type="button" id="confirmNewFile">作成</button>
54
- <button type="button" id="cancelNewFile">キャンセル</button>
55
- </div>
56
- </form>
57
- </dialog>
65
+ <footer class="footer"></footer>
58
66
  <div class="hamburger-menu">
59
67
  <input type="checkbox" id="menu-btn-check">
60
68
  <label for="menu-btn-check" class="menu-btn"><span></span></label>