md-lv 1.2.3 → 1.2.6
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/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/public/js/app.js +68 -5
- package/public/js/navigation.js +50 -6
- package/public/styles/modern.css +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.6] - 2026-02-05
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **YAML Front Matter**: Fixed support for YAML multiline string notation (`>-`, `>`, `|`, `|-`)
|
|
13
|
+
- Folded scalars (newlines replaced with spaces)
|
|
14
|
+
- Literal scalars (newlines preserved)
|
|
15
|
+
|
|
16
|
+
## [1.2.5] - 2026-02-05
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Sidebar Scroll Position**: Sidebar now preserves scroll position when navigating between files
|
|
21
|
+
- **Sticky Breadcrumbs**: Path display (breadcrumbs) now stays fixed at the top when scrolling
|
|
22
|
+
|
|
8
23
|
## [1.2.3] - 2026-01-31
|
|
9
24
|
|
|
10
25
|
### Fixed
|
package/package.json
CHANGED
package/public/js/app.js
CHANGED
|
@@ -38,15 +38,56 @@ function parseFrontmatter(markdown) {
|
|
|
38
38
|
return { frontmatter: null, body };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// YAML パース(key: value
|
|
41
|
+
// YAML パース(key: value 形式、配列形式、複数行文字列に対応)
|
|
42
42
|
const frontmatter = {};
|
|
43
43
|
const lines = yamlContent.split('\n');
|
|
44
44
|
let currentKey = null;
|
|
45
|
+
let multilineMode = null; // 'folded' (>) or 'literal' (|)
|
|
46
|
+
let multilineBuffer = [];
|
|
47
|
+
let multilineStripTrailing = false;
|
|
48
|
+
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
const line = lines[i];
|
|
51
|
+
|
|
52
|
+
// 複数行モード中の処理
|
|
53
|
+
if (multilineMode && currentKey) {
|
|
54
|
+
// インデントされた行は複数行コンテンツの一部
|
|
55
|
+
if (line.match(/^[ \t]+\S/) || line.trim() === '') {
|
|
56
|
+
// インデントを除去して保存
|
|
57
|
+
const indentMatch = line.match(/^([ \t]+)(.*)$/);
|
|
58
|
+
if (indentMatch) {
|
|
59
|
+
multilineBuffer.push(indentMatch[2]);
|
|
60
|
+
} else if (line.trim() === '') {
|
|
61
|
+
multilineBuffer.push('');
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
} else {
|
|
65
|
+
// インデントされていない行は複数行モードの終了
|
|
66
|
+
let result = multilineBuffer.join('\n');
|
|
67
|
+
|
|
68
|
+
if (multilineMode === 'folded') {
|
|
69
|
+
// 改行を空白に置き換え(空行は段落区切りとして保持)
|
|
70
|
+
result = result.replace(/\n(?!\n)/g, ' ').replace(/\n\n+/g, '\n\n');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 末尾の改行を処理
|
|
74
|
+
if (multilineStripTrailing) {
|
|
75
|
+
result = result.replace(/\n+$/, '');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
frontmatter[currentKey] = result.trim();
|
|
79
|
+
multilineMode = null;
|
|
80
|
+
multilineBuffer = [];
|
|
81
|
+
multilineStripTrailing = false;
|
|
82
|
+
// 現在の行を再処理するためにインデックスを戻す
|
|
83
|
+
i--;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
45
87
|
|
|
46
|
-
for (const line of lines) {
|
|
47
88
|
// 配列アイテム(- value)のチェック
|
|
48
89
|
const arrayItemMatch = line.match(/^[ \t]+-[ \t]+(.*)$/);
|
|
49
|
-
if (arrayItemMatch && currentKey) {
|
|
90
|
+
if (arrayItemMatch && currentKey && !multilineMode) {
|
|
50
91
|
const itemValue = parseYamlValue(arrayItemMatch[1]);
|
|
51
92
|
if (!Array.isArray(frontmatter[currentKey])) {
|
|
52
93
|
frontmatter[currentKey] = [];
|
|
@@ -63,15 +104,37 @@ function parseFrontmatter(markdown) {
|
|
|
63
104
|
|
|
64
105
|
if (key) {
|
|
65
106
|
currentKey = key;
|
|
66
|
-
|
|
107
|
+
|
|
108
|
+
// 複数行文字列の開始記号をチェック
|
|
109
|
+
if (value === '>' || value === '>-' || value === '|' || value === '|-') {
|
|
110
|
+
multilineMode = (value.startsWith('>') ? 'folded' : 'literal');
|
|
111
|
+
multilineStripTrailing = value.endsWith('-');
|
|
112
|
+
multilineBuffer = [];
|
|
113
|
+
} else if (value) {
|
|
67
114
|
// 同じ行に値がある場合
|
|
68
115
|
frontmatter[key] = parseYamlValue(value);
|
|
116
|
+
currentKey = key; // 配列の可能性のためにキーを保持
|
|
69
117
|
}
|
|
70
|
-
//
|
|
118
|
+
// 値が空の場合は次の行で配列または複数行として処理される
|
|
71
119
|
}
|
|
72
120
|
}
|
|
73
121
|
}
|
|
74
122
|
|
|
123
|
+
// ファイル終端で複数行モードが残っている場合
|
|
124
|
+
if (multilineMode && currentKey && multilineBuffer.length > 0) {
|
|
125
|
+
let result = multilineBuffer.join('\n');
|
|
126
|
+
|
|
127
|
+
if (multilineMode === 'folded') {
|
|
128
|
+
result = result.replace(/\n(?!\n)/g, ' ').replace(/\n\n+/g, '\n\n');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (multilineStripTrailing) {
|
|
132
|
+
result = result.replace(/\n+$/, '');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
frontmatter[currentKey] = result.trim();
|
|
136
|
+
}
|
|
137
|
+
|
|
75
138
|
return { frontmatter, body };
|
|
76
139
|
}
|
|
77
140
|
|
package/public/js/navigation.js
CHANGED
|
@@ -50,10 +50,33 @@ function addExpandedDirectory(path) {
|
|
|
50
50
|
*/
|
|
51
51
|
function removeExpandedDirectory(path) {
|
|
52
52
|
const expanded = getExpandedDirectories();
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
// 閉じたフォルダとその子孫フォルダを全て削除
|
|
54
|
+
const filtered = expanded.filter(p => !p.startsWith(path));
|
|
55
|
+
saveExpandedDirectories(filtered);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* サイドバーのスクロール位置をlocalStorageに保存
|
|
60
|
+
*/
|
|
61
|
+
function saveSidebarScrollPosition(scrollTop) {
|
|
62
|
+
try {
|
|
63
|
+
localStorage.setItem('sidebar-scroll-position', scrollTop.toString());
|
|
64
|
+
} catch {
|
|
65
|
+
// Storage full or unavailable
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* サイドバーのスクロール位置をlocalStorageから取得して復元
|
|
71
|
+
*/
|
|
72
|
+
function restoreSidebarScrollPosition(element) {
|
|
73
|
+
try {
|
|
74
|
+
const savedScrollTop = localStorage.getItem('sidebar-scroll-position');
|
|
75
|
+
if (savedScrollTop !== null) {
|
|
76
|
+
element.scrollTop = parseInt(savedScrollTop, 10);
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
// Storage unavailable
|
|
57
80
|
}
|
|
58
81
|
}
|
|
59
82
|
|
|
@@ -140,6 +163,23 @@ function initSidebar() {
|
|
|
140
163
|
// 現在のパスを取得
|
|
141
164
|
const currentPath = window.location.pathname;
|
|
142
165
|
|
|
166
|
+
// サイドバーコンテンツのスクロール位置を復元・保存
|
|
167
|
+
const sidebarContent = document.querySelector('.sidebar-content');
|
|
168
|
+
if (sidebarContent) {
|
|
169
|
+
// ページロード時にスクロール位置を復元
|
|
170
|
+
restoreSidebarScrollPosition(sidebarContent);
|
|
171
|
+
|
|
172
|
+
// スクロール時にスクロール位置を保存
|
|
173
|
+
sidebarContent.addEventListener('scroll', () => {
|
|
174
|
+
saveSidebarScrollPosition(sidebarContent.scrollTop);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ページ遷移前にスクロール位置を保存
|
|
178
|
+
window.addEventListener('beforeunload', () => {
|
|
179
|
+
saveSidebarScrollPosition(sidebarContent.scrollTop);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
143
183
|
// ルートディレクトリを読み込み
|
|
144
184
|
loadDirectory('/', fileTree, 0).then(async () => {
|
|
145
185
|
// 保存された展開状態を復元
|
|
@@ -147,6 +187,11 @@ function initSidebar() {
|
|
|
147
187
|
for (const dirPath of expandedDirs) {
|
|
148
188
|
await expandToPath(dirPath, fileTree);
|
|
149
189
|
}
|
|
190
|
+
|
|
191
|
+
// ツリーの読み込み後、再度スクロール位置を復元(コンテンツが変わった場合に備えて)
|
|
192
|
+
if (sidebarContent) {
|
|
193
|
+
restoreSidebarScrollPosition(sidebarContent);
|
|
194
|
+
}
|
|
150
195
|
});
|
|
151
196
|
}
|
|
152
197
|
|
|
@@ -322,8 +367,7 @@ async function expandToPath(targetPath, container) {
|
|
|
322
367
|
if (chevron && children && !chevron.classList.contains('expanded')) {
|
|
323
368
|
chevron.classList.add('expanded');
|
|
324
369
|
children.classList.add('expanded');
|
|
325
|
-
//
|
|
326
|
-
addExpandedDirectory(itemPath);
|
|
370
|
+
// 注: 復元時は中間パスを保存しない(ユーザー操作時のみ保存)
|
|
327
371
|
|
|
328
372
|
if (children.children.length === 0) {
|
|
329
373
|
const loading = document.createElement('li');
|