```dataviewjs
class PersistentData {
	static #constructKey = Symbol()
	static #instance
	static _dataFilePath
	static _defaultData
	_dataFilePath
	_defaultData
	_data
	constructor(constructKey, dataFilePath, defaultData) {
		if (constructKey !== PersistentData.#constructKey)
			throw new Error('constructor is private')
		this._dataFilePath = dataFilePath
		this._defaultData = defaultData
	}
	static setConfig(dataFilePath, dafaultData){
		this._dataFilePath = dataFilePath
		this._defaultData = dafaultData
	}
	static async getInstance(){
		if (!this.#instance){
			this.#instance = new PersistentData(
				this.#constructKey,
				this._dataFilePath,
				this._defaultData
			)
			await this.#instance.initialize()
		}
		return this.#instance
	}
	async initialize(){
		const exists = await app.vault.adapter.exists(this._dataFilePath)
		if (exists) {
			const str = await app.vault.adapter.read(this._dataFilePath)
			const data = JSON.parse(str)
			// 保存されているデータとdefaultDataのキーが不一致の場合、データの仕様が変わったと判断し、保存データをdefaultDataにリセットする
			if (this._haveSameKeys(data, this._defaultData)){
				this._data = data
				return
			}
		}
		this._data = this._defaultData
		await this.saveData()
	}
	getData(key){
		return this._data[key]
	}
	async setData(key, value){
		this._data[key] = value
		await this.saveData()
	}
	async saveData(){
		await app.vault.adapter.write(this._dataFilePath, this._toJSON(this._data))
	}
	_haveSameKeys(obj1, obj2) {
		const keys1 = Object.keys(obj1).sort()
		const keys2 = Object.keys(obj2).sort()
		return keys1.length === keys2.length && keys1.every((key, index) => key === keys2[index])
	}
	_toJSON(obj){
		return JSON.stringify(obj, null, 2)
	}
}

function debounce(func, wait) {
	let timeout = null
	return function(...args) {
		if (timeout)
			clearTimeout(timeout)
		timeout = setTimeout(() => {
			func(...args)
			timeout = null
		}, wait)
	}
}

function escapeHtml(str) {
	return str
		.replace(/&/g, "&")
		.replace(/</g, "&lt;")
		.replace(/>/g, "&gt;")
		.replace(/"/g, "&quot;")
		.replace(/'/g, "&#039;")
}

async function updateList() {
	const daysBack = selectDays.value
	const now = dv.date('now')
	let pages

	const startDate = now.minus({ days: parseInt(daysBack) - 1 }).startOf('day')
	pages = dv.pages()
		.where(p => {
			const mtime = dv.date(p.file.mtime)
			return mtime >= startDate && mtime <= now
		})

    if (searchString)
		pages = pages.where(p => p.file.name.toLowerCase().includes(searchString.toLowerCase()))

	const folderCounts = {}
	pages.forEach(page => {
		const folder = page.file.folder || ''
		folderCounts[folder] = (folderCounts[folder] || 0) + 1
	})

	const folders = Array.from(pages.file.folder.distinct()).sort()
	folders.unshift('すべてのフォルダ')

	if (!folders.includes(currentFolder)){
		currentFolder = 'すべてのフォルダ'
		await persistentData.setData('currentFolder', currentFolder)
	}

	selectFolder.innerHTML = ''
	folders.forEach(folder => {
		const count = folder === 'すべてのフォルダ' ? pages.length : (folderCounts[folder] || 0)
		const displayText = folder === 'すべてのフォルダ' ? `${folder} (${count})` : `${folder} (${count})`
		const option = dv.el('option', displayText, { container: selectFolder })
		option.value = folder
		if (folder === currentFolder)
			option.selected = true
	})

    let filteredPages = pages
    if (currentFolder !== 'すべてのフォルダ')
        filteredPages = pages.where(p => p.file.folder === currentFolder)

	const liList = []
	filteredPages
		.sort(p => p.file.mtime, 'desc')
		.forEach((page) => {
			liList.push('<li><a data-url="' + escapeHtml(page.file.path) + '">' + escapeHtml(page.file.name) + '</a></li>')
		})
	listContainer.innerHTML = '<ul>' + liList.join('') + '</ul>'
}

const config = {
	persistentData: {
		// 永続データ保存ファイルのVault内パス
		// 親ディレクトリは前もって作成しておく
		dataFilePath: '/Scripts/Dataview/PersistentData/touchSort.json',

		// 永続データのデフォルト値
		defaultData: {selectDaysValue: 10, currentFolder: 'すべてのフォルダ', searchString: ''}
	},
	// 日数の選択肢
	selectDaysOptions: [1, 2, 3, 5, 7, 10, 14, 20, 30, 60]
}

PersistentData.setConfig(
	config.persistentData.dataFilePath,
	config.persistentData.defaultData
)
const persistentData = await PersistentData.getInstance()

let currentFolder = persistentData.getData('currentFolder')
let searchString = persistentData.getData('searchString')

const selectDays = dv.el('select', '')
const options = config.selectDaysOptions
options.forEach(option => {
    const opt = dv.el('option', `${option}日`, { container: selectDays })
    opt.value = option
})

const selectFolder = dv.el('select', '')
const searchInput = dv.el('input', '', { attr: { type: 'text', placeholder: '検索', value: searchString} })
const listContainer = dv.el('div', '')

listContainer.addEventListener('click',(e)=>{
	if (e.target.tagName === 'A')
		app.workspace.openLinkText(e.target.dataset.url, '', true)
})

selectDays.addEventListener('change', async () => {
	updateList()
	await persistentData.setData('selectDaysValue', selectDays.value)
})

selectFolder.addEventListener('change', async () => {
    currentFolder = selectFolder.value
    updateList()
	await persistentData.setData('currentFolder', currentFolder)
})

searchInput.addEventListener('input', debounce(async () => {
    searchString = searchInput.value.trim()
    updateList()
	await persistentData.setData('searchString', searchString)
}, 1000))

selectDays.value = persistentData.getData('selectDaysValue')
selectFolder.value = currentFolder
updateList()

```