# Obsidian 公开库从 Docsify 改为 Docute
最近把 Obsidian 公开库从 Docsify 折腾为了 Docute。
## 缘起
大概是 2022 年 9 月,我使用 Obsidian Remotely Save + 对象存储静态网站托管 + Docsify 搭建了一个公开库。也放了一些静态网站,后来一些文件也被用于博客网站的数据源。
这其中,Docsify 只需要一个 index.html 和一些 markdown 文件就可以部署为网站。需要做的就是一些索引工作。这之前,我使用的是 Zoottelkeeper 生成索引链接。这其实并不能完全符合我的要求,但是也不想自己折腾插件,于是也就这样了。
2024-02-21 又看到了 Docute ,顺手把 Docsify 改为 Docute 。效果 https://note.ftls.xyz/#/
## Docute
### Docute 介绍
Docute https://docute.egoist.dev/zh/
官网的话是
> Docute 本质上就是一个 JavaScript 文件,它可以获取 Markdown 文件并将它们呈现为单页面应用。
它完全由运行时驱动,因此并不涉及服务端组件,这就意味着没有构建过程。你只需创建一个 HTML 文件和一堆 Markdown 文档,你的网站就差不多完成了!
> Docsify 和 Docute 几乎相同,但具有不同的 UI 和不同的使用方式。
Docute(60kB)比 Docisfy(20kB)大 3 倍,因为我们使用了 Vue,Vue Router 和 Vuex,而 Docsify 使用的是 vanilla JavaScript。
实际上这个大小数据已经过时了,Docute 现在 200kb+ 。 如果自己写的自己需要的功能,大概会小一些。
虽然 200kb 很大,但我今天才发现我用的 Docsify icon 就 60kb+

### 侧边栏
Docute 功能上很多都是打包好的,js 比 Docisfy 大,配置项却比 Docisfy 少,倒是也够用了。使用上最大的区别是侧边栏生成方式不同,Docsify 使用的是 `.md` 文件渲染。并且似乎在内部修改了渲染 markdown list 的方法,在 index.html 重新定义渲染方法会导致侧边栏没有 css 效果。 不过问题不大,我也不用侧边栏,只是一个文档站点。正好 Docute 没有这个问题。
不过使用 js 对象, Zoottelkeeper 的插件生成的文件是满足不了 Docute 的需求了。于是我选择了 RunJS 插件生成文件,来为 Docute 提供侧边栏。
在 Obsidian 中一个 .md 文件中写入下面代码,根目录创建 `sidebar.md`。然后带点侧边 JS 图标,运行 List 就行了。程序会读取 markdown 文件,并把最后对象写入文件,给 Docute 使用。
`sidebar.md` 虽然是 markdown 文件,但是确实是 JSON 内容,写成 markdown 是为了在 Obsidian 就可以观察到变化。
RunJS 是调用的 Obsidian 的 API ,Obsidian 文件列出,创建,修改 API 参考 https://docs.obsidian.md/Reference/TypeScript+API/Vault
````md
```js RunJS="List"
new Notice("生成索引文件");
// import * as obsidian from 'obsidian';
const runJS = this;
let pagesFile = [];
let journalsFiles = [];
let ZKFiles = [];
this.app.vault.getMarkdownFiles().map((file) => {
if(file.path.startsWith("pages/")) {
pagesFile.push({
title : file.path.replace("pages/","").replace(".md",""),
link : "/"+file.path.slice(0,file.path.length-3),
});
}
if(file.path.startsWith("journals/")) {
journalsFiles.push({
title : file.path.replace("journals/","").replace(".md",""),
link : "/"+file.path.slice(0,file.path.length-3),
});
}
if(file.path.startsWith("ZK/")) {
ZKFiles.push({
title : file.path.replace("ZK/","").replace(".md",""),
link : "/"+file.path.slice(0,file.path.length-3),
});
}
// new Notice(file.path);
// result += file.path + "\n";
})
let obj = [
{
title: "Pages",
children: pagesFile
},
{
title: "Journals",
children: journalsFiles
},
{
title: "ZK",
children: ZKFiles
}
]
const xxxx = this.app.vault.getAbstractFileByPath("sidebar.md");
this.app.vault.modify(xxxx, JSON.stringify(obj)).then(res => {
new Notice("Finish!");
})
```
````
Obsidian 库根目录下的 index.html 为
```html
笔记
```
这样每次创建 .md 文件,RunJS 运行下 List,Remotely Save 同步到对象存储,网站刷新一下就能看到了。
### 时间顺序
后面我把侧边栏按照创建时间排序,使最新的在上面。
```js
let files = this.app.vault.getMarkdownFiles();
files.sort((a,b) => {
//new Notice(a.stat.ctime - b.stat.ctime);
return b.stat.ctime - a.stat.ctime;
});
files.map((file) => {})
```
### sw.js
然后我又加上了 sw.js 。不过 Docute 文档里的离线支持,使用的 Google 服务我打不开。用的还是 Docsify 之前的 sw.js
```js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw-docsify.js')
}
```
```js
/* ===========================================================
* docsify sw.js
* ===========================================================
* Copyright 2016 @huxpro
* Licensed under Apache 2.0
* Register service worker.
* ========================================================== */
const RUNTIME = 'docsify'
const HOSTNAME_WHITELIST = [
self.location.hostname,
'fonts.gstatic.com',
'fonts.googleapis.com',
'cdn.jsdelivr.net'
]
// The Util Function to hack URLs of intercepted requests
const getFixedUrl = (req) => {
var now = Date.now()
var url = new URL(req.url)
// 1. fixed http URL
// Just keep syncing with location.protocol
// fetch(httpURL) belongs to active mixed content.
// And fetch(httpRequest) is not supported yet.
url.protocol = self.location.protocol
// 2. add query for caching-busting.
// Github Pages served with Cache-Control: max-age=600
// max-age on mutable content is error-prone, with SW life of bugs can even extend.
// Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
// Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
if (url.hostname === self.location.hostname) {
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
}
return url.href
}
/**
* @Lifecycle Activate
* New one activated when old isnt being used.
*
* waitUntil(): activating ====> activated
*/
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
/**
* @Functional Fetch
* All network requests are being intercepted here.
*
* void respondWith(Promise r)
*/
self.addEventListener('fetch', event => {
// Skip some of cross-origin requests, like those for Google Analytics.
if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
// Stale-while-revalidate
// similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
// Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
const cached = caches.match(event.request)
const fixedUrl = getFixedUrl(event.request)
const fetched = fetch(fixedUrl, { cache: 'no-store' })
const fetchedCopy = fetched.then(resp => resp.clone())
// Call respondWith() with whatever we get first.
// If the fetch fails (e.g disconnected), wait for the cache.
// If there’s nothing in cache, wait for the fetch.
// If neither yields a response, return offline pages.
event.respondWith(
Promise.race([fetched.catch(_ => cached), cached])
.then(resp => resp || fetched)
.catch(_ => { /* eat any errors */ })
)
// Update the cache with the version we fetched (only for ok status)
event.waitUntil(
Promise.all([fetchedCopy, caches.open(RUNTIME)])
.then(([response, cache]) => response.ok && cache.put(event.request, response))
.catch(_ => { /* eat any errors */ })
)
}
})
```
## 总结
大概就是这些。另外,我这 BearBlog 学到一种用 Emoji 表情作为 icon 的代码,如下:
```html
```
放在 HTML `` 里面就行了。
Docute 看起来还不错,UI 挺耐看的。不过可能哪天我又换成 Docsify 了,或者其他什么。
Docute 侧边栏是不支持多级目录的,也就是 children 层数有限,就想起了 [Naive UI 树](https://www.naiveui.com/zh-CN/os-theme/components/tree) 看着不错了。我之前就觉得 Naive UI 确实很好看来着,可能哪天用 Naive UI 写一个类似的东西。