@commte/mdbrowse 0.2.1 → 0.3.1
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/README.ja.md +10 -0
- package/README.md +10 -0
- package/assets/head.html +18 -7
- package/bin/mdbrowse +169 -34
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -59,12 +59,22 @@ mdb
|
|
|
59
59
|
|
|
60
60
|
プレビュー用のタブが開き、小さな背景プロセスが動きだす。以後どの Markdown を保存しても、どのプロジェクトのものでも、そのタブに出る。止めるときは `mdb --stop`、再開はまた `mdb`
|
|
61
61
|
|
|
62
|
+
ログアウトや再起動で消える。macOS なら、勝手に戻ってくるようにもできる
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
mdb --install-agent
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`~/Library/LaunchAgents` に LaunchAgent を1枚置くだけで、ログインした時点から追従が動いている。外すときは `mdb --uninstall-agent`。macOS のログイン項目には「開発元を識別できない項目」として出る。スクリプトに署名していないため
|
|
69
|
+
|
|
62
70
|
## 使い方
|
|
63
71
|
|
|
64
72
|
```sh
|
|
65
73
|
mdb # タブを開いて、以後ずっと追従させる
|
|
66
74
|
mdb --stop # 背景の追従を止める
|
|
67
75
|
mdb --status # いま何が出ているかを表示する
|
|
76
|
+
mdb --install-agent # ログイン時に自動で始めるようにする(macOS)
|
|
77
|
+
mdb --uninstall-agent # それを外す
|
|
68
78
|
mdb file.md # そのファイルを1回だけ変換する
|
|
69
79
|
mdb -w file.md # 1つのファイルだけを前面で監視する(Ctrl-C で終了)
|
|
70
80
|
mdb --path # 出力先の HTML のパスを表示する
|
package/README.md
CHANGED
|
@@ -59,12 +59,22 @@ mdb
|
|
|
59
59
|
|
|
60
60
|
That opens the preview tab and starts a small background process. Save any Markdown file from here on and it shows up in that tab, whichever project it lives in. `mdb --stop` ends it; `mdb` starts it again.
|
|
61
61
|
|
|
62
|
+
The process is gone after a logout or a reboot. On macOS you can have it come back on its own:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
mdb --install-agent
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
That writes a LaunchAgent under `~/Library/LaunchAgents` and nothing else, so the sync is running from the moment you log in. `mdb --uninstall-agent` removes it. macOS lists it under Login Items as an unidentified developer — the script is not code-signed.
|
|
69
|
+
|
|
62
70
|
## Usage
|
|
63
71
|
|
|
64
72
|
```sh
|
|
65
73
|
mdb # open the tab and keep it in sync
|
|
66
74
|
mdb --stop # stop the background sync
|
|
67
75
|
mdb --status # show what is being previewed
|
|
76
|
+
mdb --install-agent # start the sync at every login (macOS)
|
|
77
|
+
mdb --uninstall-agent # remove it
|
|
68
78
|
mdb file.md # render one file, once
|
|
69
79
|
mdb -w file.md # follow one file in the foreground (Ctrl-C to stop)
|
|
70
80
|
mdb --path # print the output HTML path
|
package/assets/head.html
CHANGED
|
@@ -184,10 +184,17 @@
|
|
|
184
184
|
var THEMES = ['light', 'dark'];
|
|
185
185
|
var WIDTHS = [800, 680, 960, 1200];
|
|
186
186
|
var printing = false;
|
|
187
|
-
|
|
188
|
-
addEventListener('
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
var printTimer = null;
|
|
188
|
+
addEventListener('beforeprint', function () {
|
|
189
|
+
printing = true;
|
|
190
|
+
// afterprint が来ないブラウザや、途中で閉じられた場合に止まったままにならないようにする
|
|
191
|
+
clearTimeout(printTimer);
|
|
192
|
+
printTimer = setTimeout(function () { printing = false; }, 60000);
|
|
193
|
+
});
|
|
194
|
+
addEventListener('afterprint', function () {
|
|
195
|
+
printing = false;
|
|
196
|
+
clearTimeout(printTimer);
|
|
197
|
+
});
|
|
191
198
|
|
|
192
199
|
function save() { try { localStorage.setItem('mdbrowse', JSON.stringify(s)); } catch (e) {} }
|
|
193
200
|
function fontSize() { return s.fontSize || parseInt(getComputedStyle(r).getPropertyValue('--font-size')) || 16; }
|
|
@@ -257,7 +264,8 @@
|
|
|
257
264
|
}
|
|
258
265
|
heads = heads.filter(function (h) { return lv(h) <= top + 1; });
|
|
259
266
|
}
|
|
260
|
-
|
|
267
|
+
try {
|
|
268
|
+
if (heads.length >= 3 && typeof IntersectionObserver === 'function') {
|
|
261
269
|
var topLevel = Math.min.apply(null, heads.map(function (h) { return +h.tagName.slice(1); }));
|
|
262
270
|
var toc = document.createElement('nav');
|
|
263
271
|
toc.id = 'mdtoc';
|
|
@@ -294,6 +302,7 @@
|
|
|
294
302
|
}, { rootMargin: '0px 0px -75% 0px' });
|
|
295
303
|
heads.forEach(function (h) { io.observe(h); });
|
|
296
304
|
}
|
|
305
|
+
} catch (e) { /* 目次が組めなくても、更新の監視は動かす */ }
|
|
297
306
|
|
|
298
307
|
// スクロール位置はブラウザ標準の復元に任せる(手で保存するとカクつく)
|
|
299
308
|
var lastScroll = 0;
|
|
@@ -318,6 +327,7 @@
|
|
|
318
327
|
var stampUrl = location.pathname.replace(/\.html$/, '-stamp.js');
|
|
319
328
|
var known = null;
|
|
320
329
|
function checkStamp() {
|
|
330
|
+
if (printing) return;
|
|
321
331
|
var el = document.createElement('script');
|
|
322
332
|
el.src = stampUrl + '?t=' + Date.now();
|
|
323
333
|
el.onload = function () {
|
|
@@ -335,11 +345,12 @@
|
|
|
335
345
|
addEventListener('focus', function () { checkStamp(); });
|
|
336
346
|
// バーを操作している間は再読み込みを止める。状態を持たずにその場で見る
|
|
337
347
|
// (持つと、バーの上にポインタを置いたままウィンドウを離れたときに止まったままになる)
|
|
338
|
-
var hoverable =
|
|
348
|
+
var hoverable = typeof window.matchMedia !== 'function'
|
|
349
|
+
|| window.matchMedia('(hover: hover)').matches;
|
|
339
350
|
setInterval(function () {
|
|
340
351
|
if (printing || (hoverable && bar.matches(':hover'))) return;
|
|
341
352
|
if (Date.now() - lastScroll < 900) return;
|
|
342
353
|
checkStamp();
|
|
343
|
-
},
|
|
354
|
+
}, 250);
|
|
344
355
|
});
|
|
345
356
|
</script>
|
package/bin/mdbrowse
CHANGED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
# browser tab can stay open and just swap its contents.
|
|
4
4
|
set -euo pipefail
|
|
5
5
|
|
|
6
|
-
VERSION="0.
|
|
6
|
+
VERSION="0.3.1"
|
|
7
7
|
PROG="$(basename "$0")"
|
|
8
8
|
OUT="${MDBROWSE_OUT:-/tmp/mdbrowse.html}"
|
|
9
|
+
case "$OUT" in
|
|
10
|
+
*.html) ;;
|
|
11
|
+
*) echo "$(basename "$0"): MDBROWSE_OUT must end with .html (got: $OUT)" >&2; exit 2 ;;
|
|
12
|
+
esac
|
|
13
|
+
STAMP="${OUT%.html}-stamp.js"
|
|
9
14
|
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/mdbrowse"
|
|
10
15
|
|
|
11
16
|
# スタイルシートの探索順
|
|
@@ -40,6 +45,8 @@ Usage:
|
|
|
40
45
|
mdb <file.md> render that file once
|
|
41
46
|
mdb --stop stop the background sync
|
|
42
47
|
mdb --status show what is being previewed
|
|
48
|
+
mdb --install-agent start the sync automatically at every login (macOS)
|
|
49
|
+
mdb --uninstall-agent remove it
|
|
43
50
|
mdb -w <file|dir> watch in the foreground instead (Ctrl-C to stop)
|
|
44
51
|
mdb --eject copy the bundled stylesheet into your config directory
|
|
45
52
|
mdb --path print the output HTML path
|
|
@@ -47,7 +54,12 @@ Usage:
|
|
|
47
54
|
|
|
48
55
|
With no arguments, mdb opens the preview tab and starts a small background
|
|
49
56
|
process. From then on, whatever Markdown file you save — in any project, from
|
|
50
|
-
any editor — shows up in that tab. Nothing else to set up. `mdb --stop` ends it
|
|
57
|
+
any editor — shows up in that tab. Nothing else to set up. `mdb --stop` ends it,
|
|
58
|
+
and it is gone after a logout or a reboot.
|
|
59
|
+
|
|
60
|
+
`mdb --install-agent` makes it start on its own at every login, so you never
|
|
61
|
+
have to think about starting or stopping it. It writes a LaunchAgent under
|
|
62
|
+
~/Library/LaunchAgents and nothing else; `mdb --uninstall-agent` removes it.
|
|
51
63
|
|
|
52
64
|
Finding the file you just saved uses Spotlight (mdfind). Files under hidden
|
|
53
65
|
directories, node_modules and ~/Library are ignored. Without Spotlight, mdb
|
|
@@ -72,14 +84,28 @@ open_url() {
|
|
|
72
84
|
fi
|
|
73
85
|
}
|
|
74
86
|
|
|
87
|
+
# まだ何も変換していないときの仮ページ。スタイルシートごと入れるので、
|
|
88
|
+
# 更新の監視も目次もバーも、ふつうのページと同じように動く
|
|
89
|
+
placeholder() {
|
|
90
|
+
printf '%s\n' '<!doctype html><meta charset="utf-8"><title>mdbrowse</title>'
|
|
91
|
+
[ -f "$HEAD" ] && cat "$HEAD"
|
|
92
|
+
printf '%s\n' '<p>No preview yet. Save a Markdown file.'
|
|
93
|
+
}
|
|
94
|
+
|
|
75
95
|
open_tab() {
|
|
76
|
-
[ -f "$OUT" ] ||
|
|
77
|
-
[ -f "$
|
|
96
|
+
[ -f "$OUT" ] || placeholder > "$OUT"
|
|
97
|
+
[ -f "$STAMP" ] || printf 'window.__mdbrowseStamp="0";\n' > "$STAMP"
|
|
78
98
|
open_url
|
|
79
99
|
}
|
|
80
100
|
|
|
81
|
-
|
|
82
|
-
|
|
101
|
+
# PID・ログ・ロックは自分だけが書ける場所に置く。共有の /tmp に固定名で置くと、
|
|
102
|
+
# 先に symlink を作られたときに書き込み先を差し替えられる
|
|
103
|
+
RUNTIME_DIR="${TMPDIR:-/tmp}/mdbrowse-$(id -u)"
|
|
104
|
+
( umask 077; mkdir -p "$RUNTIME_DIR" ) 2>/dev/null || true
|
|
105
|
+
LOGFILE="$RUNTIME_DIR/sync.log"
|
|
106
|
+
LOCKDIR="$RUNTIME_DIR/sync.lock"
|
|
107
|
+
PIDFILE="$LOCKDIR/pid"
|
|
108
|
+
LEGACY_PIDFILE="${OUT%.html}-sync.pid"
|
|
83
109
|
|
|
84
110
|
running_pid() {
|
|
85
111
|
[ -f "$PIDFILE" ] || return 1
|
|
@@ -92,15 +118,27 @@ running_pid() {
|
|
|
92
118
|
}
|
|
93
119
|
|
|
94
120
|
stop_sync() {
|
|
95
|
-
local pid
|
|
121
|
+
local pid stopped=0 i=0
|
|
96
122
|
if pid="$(running_pid)"; then
|
|
97
123
|
kill "$pid" 2>/dev/null || true
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
while [ $i -lt 30 ]; do
|
|
125
|
+
kill -0 "$pid" 2>/dev/null || break
|
|
126
|
+
i=$(( i + 1 ))
|
|
127
|
+
sleep 0.1
|
|
128
|
+
done
|
|
129
|
+
stopped=1
|
|
130
|
+
fi
|
|
131
|
+
# 0.2.x が置いた古い場所のものも拾う
|
|
132
|
+
if [ -f "$LEGACY_PIDFILE" ]; then
|
|
133
|
+
local old; old="$(cat "$LEGACY_PIDFILE" 2>/dev/null || true)"
|
|
134
|
+
if [ -n "$old" ] && kill -0 "$old" 2>/dev/null; then
|
|
135
|
+
kill "$old" 2>/dev/null || true
|
|
136
|
+
stopped=1
|
|
137
|
+
fi
|
|
138
|
+
rm -f "$LEGACY_PIDFILE"
|
|
103
139
|
fi
|
|
140
|
+
rm -rf "$LOCKDIR"
|
|
141
|
+
if [ "$stopped" -eq 1 ]; then echo "$PROG: stopped"; else echo "$PROG: not running"; fi
|
|
104
142
|
}
|
|
105
143
|
|
|
106
144
|
start_sync() {
|
|
@@ -124,8 +162,74 @@ start_sync() {
|
|
|
124
162
|
fi
|
|
125
163
|
}
|
|
126
164
|
|
|
165
|
+
AGENT_LABEL="com.commte.mdbrowse"
|
|
166
|
+
AGENT_PLIST="$HOME/Library/LaunchAgents/$AGENT_LABEL.plist"
|
|
167
|
+
|
|
168
|
+
agent_installed() { [ -f "$AGENT_PLIST" ]; }
|
|
169
|
+
|
|
170
|
+
install_agent() {
|
|
171
|
+
if [ "$(uname -s)" != "Darwin" ]; then
|
|
172
|
+
echo "$PROG: --install-agent is macOS only" >&2
|
|
173
|
+
return 1
|
|
174
|
+
fi
|
|
175
|
+
local self; self="$(resolve_dir "$0")/$(basename "$(readlink "$0" 2>/dev/null || echo "$0")")"
|
|
176
|
+
[ -x "$self" ] || self="$0"
|
|
177
|
+
|
|
178
|
+
# launchd の PATH は最小限なので、pandoc と node の場所を書き込んでおく
|
|
179
|
+
local paths="/usr/bin:/bin:/usr/sbin:/sbin"
|
|
180
|
+
local d
|
|
181
|
+
for d in "$(command -v pandoc)" "$(command -v node)" "$(command -v mdfind)"; do
|
|
182
|
+
[ -n "$d" ] && paths="$(dirname "$d"):$paths"
|
|
183
|
+
done
|
|
184
|
+
|
|
185
|
+
mkdir -p "$HOME/Library/LaunchAgents"
|
|
186
|
+
{
|
|
187
|
+
printf '%s\n' '<?xml version="1.0" encoding="UTF-8"?>'
|
|
188
|
+
printf '%s\n' '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">'
|
|
189
|
+
printf '%s\n' '<plist version="1.0">'
|
|
190
|
+
printf '%s\n' '<dict>'
|
|
191
|
+
printf ' <key>Label</key><string>%s</string>\n' "$AGENT_LABEL"
|
|
192
|
+
printf '%s\n' ' <key>ProgramArguments</key>'
|
|
193
|
+
printf '%s\n' ' <array>'
|
|
194
|
+
printf ' <string>%s</string>\n' "$self"
|
|
195
|
+
printf '%s\n' ' <string>--sync-daemon</string>'
|
|
196
|
+
printf ' <string>%s</string>\n' "$HOME"
|
|
197
|
+
printf '%s\n' ' </array>'
|
|
198
|
+
printf '%s\n' ' <key>RunAtLoad</key><true/>'
|
|
199
|
+
printf '%s\n' ' <key>KeepAlive</key><false/>'
|
|
200
|
+
printf ' <key>StandardErrorPath</key><string>%s</string>\n' "$LOGFILE"
|
|
201
|
+
printf '%s\n' ' <key>EnvironmentVariables</key>'
|
|
202
|
+
printf '%s\n' ' <dict>'
|
|
203
|
+
printf ' <key>PATH</key><string>%s</string>\n' "$paths"
|
|
204
|
+
[ -n "${MDBROWSE_OUT:-}" ] && printf ' <key>MDBROWSE_OUT</key><string>%s</string>\n' "$MDBROWSE_OUT"
|
|
205
|
+
[ -n "${MDBROWSE_HEAD:-}" ] && printf ' <key>MDBROWSE_HEAD</key><string>%s</string>\n' "$MDBROWSE_HEAD"
|
|
206
|
+
printf '%s\n' ' </dict>'
|
|
207
|
+
printf '%s\n' '</dict>'
|
|
208
|
+
printf '%s\n' '</plist>'
|
|
209
|
+
} > "$AGENT_PLIST"
|
|
210
|
+
|
|
211
|
+
launchctl bootout "gui/$(id -u)/$AGENT_LABEL" 2>/dev/null || true
|
|
212
|
+
if ! launchctl bootstrap "gui/$(id -u)" "$AGENT_PLIST" 2>/dev/null; then
|
|
213
|
+
launchctl load -w "$AGENT_PLIST" 2>/dev/null || true
|
|
214
|
+
fi
|
|
215
|
+
echo "$PROG: starts at login now ($AGENT_PLIST)"
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
uninstall_agent() {
|
|
219
|
+
launchctl bootout "gui/$(id -u)/$AGENT_LABEL" 2>/dev/null \
|
|
220
|
+
|| launchctl unload -w "$AGENT_PLIST" 2>/dev/null || true
|
|
221
|
+
if [ -f "$AGENT_PLIST" ]; then
|
|
222
|
+
rm -f "$AGENT_PLIST"
|
|
223
|
+
echo "$PROG: will not start at login any more"
|
|
224
|
+
else
|
|
225
|
+
echo "$PROG: not installed"
|
|
226
|
+
fi
|
|
227
|
+
}
|
|
228
|
+
|
|
127
229
|
case "${1:-}" in
|
|
128
230
|
-h|--help) usage; exit 0 ;;
|
|
231
|
+
--install-agent) install_agent; exit $? ;;
|
|
232
|
+
--uninstall-agent) uninstall_agent; exit 0 ;;
|
|
129
233
|
-v|--version) echo "mdbrowse $VERSION"; exit 0 ;;
|
|
130
234
|
--path) echo "$OUT"; exit 0 ;;
|
|
131
235
|
--stop) stop_sync; exit 0 ;;
|
|
@@ -135,6 +239,11 @@ case "${1:-}" in
|
|
|
135
239
|
else
|
|
136
240
|
echo "$PROG: not running"
|
|
137
241
|
fi
|
|
242
|
+
if agent_installed; then
|
|
243
|
+
echo "starts at login: yes ($AGENT_PLIST)"
|
|
244
|
+
else
|
|
245
|
+
echo "starts at login: no (mdb --install-agent to change that)"
|
|
246
|
+
fi
|
|
138
247
|
[ -f "$OUT" ] && echo "showing: $(grep -o '<title>[^<]*' "$OUT" 2>/dev/null | sed 's/<title>//')"
|
|
139
248
|
exit 0 ;;
|
|
140
249
|
--eject)
|
|
@@ -228,13 +337,18 @@ else
|
|
|
228
337
|
fi
|
|
229
338
|
|
|
230
339
|
render() {
|
|
231
|
-
#
|
|
232
|
-
|
|
340
|
+
# 途中経過をタブに見せないため、別名で組み立ててから置き換える。
|
|
341
|
+
# 名前は毎回変える。固定名だと、同時に走ったときに互いの中間結果を踏む
|
|
342
|
+
local tmp
|
|
343
|
+
tmp="$(mktemp "$OUT.part.XXXXXX")" || return 1
|
|
344
|
+
|
|
233
345
|
# 出力先が .part なので、拡張子から形式を推測させず明示する
|
|
234
346
|
local args=(--standalone --from gfm --to html5 "${hl[@]}" --variable "pagetitle=$(basename "$src")" --output "$tmp")
|
|
235
347
|
[ -f "$HEAD" ] && args+=(--include-in-header "$HEAD")
|
|
236
348
|
|
|
237
|
-
|
|
349
|
+
# 途中で失敗したら、そこで止めて古い表示を残す。
|
|
350
|
+
# 関数を `render || ...` の形で呼ぶと関数の中では set -e が効かないので、各段で判定する
|
|
351
|
+
pandoc "$src" "${args[@]}" || { rm -f "$tmp"; return 1; }
|
|
238
352
|
|
|
239
353
|
# コードブロックを Shiki で色付けし直す。失敗しても変換自体は成立させる
|
|
240
354
|
if [ "$use_shiki" -eq 1 ]; then
|
|
@@ -243,14 +357,15 @@ render() {
|
|
|
243
357
|
|
|
244
358
|
# frontmatter の title を pandoc が拾うと本文の先頭に見出しが増えるので落とす。
|
|
245
359
|
# ブラウザのタブ名には pagetitle を使っている
|
|
246
|
-
perl -0pi -e 's#<header id="title-block-header">.*?</header>\n?##s' "$tmp"
|
|
360
|
+
perl -0pi -e 's#<header id="title-block-header">.*?</header>\n?##s' "$tmp" \
|
|
361
|
+
|| { rm -f "$tmp"; return 1; }
|
|
247
362
|
|
|
248
363
|
# 出力先が /tmp なので、元ファイルからの相対パス(画像・ローカルリンク)が
|
|
249
364
|
# そのままでは解決できない。ソースのあるディレクトリ基準の file:// に書き換える。
|
|
250
365
|
# 書き換えるのはタグの属性だけ。本文中の src="..." のようなインラインコードは触らない。
|
|
251
366
|
# スキーム付き(http(s) data: mailto: など)/絶対パス/ページ内アンカーはそのまま残す
|
|
252
367
|
local src_dir
|
|
253
|
-
src_dir="$(cd "$(dirname "$src")" && pwd)" || return 1
|
|
368
|
+
src_dir="$(cd "$(dirname "$src")" && pwd)" || { rm -f "$tmp"; return 1; }
|
|
254
369
|
MDBROWSE_SRC_DIR="$src_dir" perl -0pi -e '
|
|
255
370
|
BEGIN {
|
|
256
371
|
$base = $ENV{MDBROWSE_SRC_DIR};
|
|
@@ -262,21 +377,26 @@ render() {
|
|
|
262
377
|
$base =~ s/"/%22/g;
|
|
263
378
|
$base =~ s/ /%20/g;
|
|
264
379
|
}
|
|
265
|
-
|
|
380
|
+
# 属性値の中の > でタグを切らないよう、引用符の中はまとめて食う
|
|
381
|
+
s{(<(?:img|a|source|video|audio|embed|object|track)\b)((?:[^>"\x27]|"[^"]*"|\x27[^\x27]*\x27)*)(>)}{
|
|
266
382
|
my ($tag, $attrs, $close) = ($1, $2, $3);
|
|
267
383
|
$attrs =~ s{((?:src|href|poster|data)=")(?![a-zA-Z][a-zA-Z0-9+.\-]*:|//|/|\#|")([^"]*)"}
|
|
268
384
|
{$1 . "file://" . $base . "/" . $2 . "\""}ge;
|
|
385
|
+
$attrs =~ s{((?:src|href|poster|data)=\x27)(?![a-zA-Z][a-zA-Z0-9+.\-]*:|//|/|\#|\x27)([^\x27]*)\x27}
|
|
386
|
+
{$1 . "file://" . $base . "/" . $2 . "\x27"}ge;
|
|
269
387
|
$tag . $attrs . $close;
|
|
270
388
|
}gse;
|
|
271
|
-
' "$tmp"
|
|
389
|
+
' "$tmp" || { rm -f "$tmp"; return 1; }
|
|
272
390
|
|
|
273
|
-
mv -f "$tmp" "$OUT"
|
|
391
|
+
mv -f "$tmp" "$OUT" || { rm -f "$tmp"; return 1; }
|
|
274
392
|
|
|
275
393
|
# 更新の目印。ブラウザはこれを見て、中身が変わったときだけ読み直す。
|
|
276
394
|
# 本体の差し替えが済んでから書く
|
|
277
|
-
local
|
|
278
|
-
|
|
279
|
-
|
|
395
|
+
local stamp_tmp
|
|
396
|
+
stamp_tmp="$(mktemp "$STAMP.part.XXXXXX")" || return 1
|
|
397
|
+
printf 'window.__mdbrowseStamp="%s";\n' "$(date +%s)-$RANDOM" > "$stamp_tmp" \
|
|
398
|
+
|| { rm -f "$stamp_tmp"; return 1; }
|
|
399
|
+
mv -f "$stamp_tmp" "$STAMP" || { rm -f "$stamp_tmp"; return 1; }
|
|
280
400
|
}
|
|
281
401
|
|
|
282
402
|
# ファイルの更新時刻とサイズ。同じ秒内の2回目の保存も拾えるようにサイズも見る
|
|
@@ -311,10 +431,23 @@ newest_global() {
|
|
|
311
431
|
|
|
312
432
|
# 背景で動く本体。保存された Markdown を追いかけて変換し続ける
|
|
313
433
|
if [ "$daemon" -eq 1 ]; then
|
|
314
|
-
#
|
|
315
|
-
if
|
|
434
|
+
# 確認と取得を1操作にする。mkdir は原子的なので、同時に叩かれても1つしか通らない
|
|
435
|
+
if ! mkdir "$LOCKDIR" 2>/dev/null; then
|
|
436
|
+
# 取れなかった側は、持ち主が名乗るのを少し待つ。
|
|
437
|
+
# すぐ「持ち主なし」と判断すると、書き込み前の相手からロックを奪ってしまう
|
|
438
|
+
i=0
|
|
439
|
+
while [ $i -lt 30 ]; do
|
|
440
|
+
running_pid >/dev/null && exit 0
|
|
441
|
+
i=$(( i + 1 ))
|
|
442
|
+
sleep 0.1
|
|
443
|
+
done
|
|
444
|
+
# 3秒待っても名乗らないなら、置き去りのロックとして回収する
|
|
445
|
+
rm -rf "$LOCKDIR"
|
|
446
|
+
mkdir "$LOCKDIR" 2>/dev/null || exit 0
|
|
447
|
+
fi
|
|
316
448
|
echo $$ > "$PIDFILE"
|
|
317
|
-
|
|
449
|
+
# 自分が持っているときだけ外す。あとから起きたものの分を消さないため
|
|
450
|
+
trap 'if [ "$(cat "$PIDFILE" 2>/dev/null)" = "$$" ]; then rm -rf "$LOCKDIR"; fi' EXIT
|
|
318
451
|
use_spotlight=0
|
|
319
452
|
command -v mdfind >/dev/null 2>&1 && use_spotlight=1
|
|
320
453
|
if [ "$use_spotlight" -eq 0 ]; then root="$sync_cwd"; fi
|
|
@@ -326,16 +459,18 @@ if [ "$daemon" -eq 1 ]; then
|
|
|
326
459
|
# 変換の失敗は画面に出ないので、ログに残す
|
|
327
460
|
log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >> "$LOGFILE"; }
|
|
328
461
|
|
|
329
|
-
#
|
|
330
|
-
|
|
462
|
+
# 表示中のファイルは 0.25 秒ごとに見る。1周 = 0.25 秒
|
|
463
|
+
TICK=0.25
|
|
464
|
+
# Spotlight への問い合わせは 2 秒ごと(8周に1回)
|
|
465
|
+
scan_gap=8
|
|
331
466
|
if [ "$use_spotlight" -eq 0 ]; then
|
|
332
467
|
t0="$(perl -MTime::HiRes=time -e 'printf "%d", time*1000')"
|
|
333
468
|
newest_md "$root" >/dev/null
|
|
334
469
|
t1="$(perl -MTime::HiRes=time -e 'printf "%d", time*1000')"
|
|
335
|
-
scan_gap=$(( ( (t1 - t0) * 5 +
|
|
336
|
-
[ "$scan_gap" -lt
|
|
337
|
-
[ "$scan_gap" -gt
|
|
338
|
-
log "watching $root every $
|
|
470
|
+
scan_gap=$(( ( (t1 - t0) * 5 + 249) / 250 ))
|
|
471
|
+
[ "$scan_gap" -lt 8 ] && scan_gap=8
|
|
472
|
+
[ "$scan_gap" -gt 40 ] && scan_gap=40
|
|
473
|
+
log "watching $root every $(( scan_gap / 4 ))s (no Spotlight)"
|
|
339
474
|
fi
|
|
340
475
|
|
|
341
476
|
src="$(pick)"
|
|
@@ -343,7 +478,7 @@ if [ "$daemon" -eq 1 ]; then
|
|
|
343
478
|
last="$src $(fingerprint "$src") $(fingerprint "$HEAD")"
|
|
344
479
|
# 開いているファイルの保存は毎秒見る。別ファイルへの切り替えの検出は2秒ごと
|
|
345
480
|
tick=0
|
|
346
|
-
while sleep
|
|
481
|
+
while sleep "$TICK"; do
|
|
347
482
|
tick=$(( tick + 1 ))
|
|
348
483
|
if [ $(( tick % scan_gap )) -eq 0 ]; then
|
|
349
484
|
cand="$(pick)"
|
|
@@ -375,7 +510,7 @@ render
|
|
|
375
510
|
if [ "$watch" -eq 1 ]; then
|
|
376
511
|
# ディレクトリを見る場合、木が大きいと走査が重い。1回の走査時間から
|
|
377
512
|
# 待ち時間を決めて、CPU を食い潰さないようにする
|
|
378
|
-
interval=
|
|
513
|
+
interval=0.25
|
|
379
514
|
if [ -n "$root" ]; then
|
|
380
515
|
t0="$(perl -MTime::HiRes=time -e 'printf "%d", time*1000')"
|
|
381
516
|
newest_md "$root" >/dev/null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commte/mdbrowse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Preview the Markdown file you are editing in a real browser, with your own CSS, from any editor that can run a shell command. No server, no port.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mdbrowse": "bin/mdbrowse",
|