diff --git a/README.md b/README.md index e6e31c3..a00275b 100644 --- a/README.md +++ b/README.md @@ -16,13 +16,23 @@ Open a local folder and explore your files inside the browser. Your files stay p - Install Peekaboo as PWA. It works offline too. - Add a `README.md` file to display its contents above the listed directory entries. - Filter current directory entries by search term. -- Filter files and folders in current directory by tag. Add tags (comma separated) in square brackets at the end of the file or folder name. Examples: - ``` - Wallpapers [Dark, Space] - Wallpapers [Abstract, Dark] - IMG001 [2021, Berlin].jpg - IMG002 [2022, New York].jpg - ``` +- Filter files and folders in current directory by tag. + - Add a `README.md` file with frontmatter (including a `tags` property) to the desired directory. File contents example: + ```markdown + --- + tags: [Dark, Space] + --- + # Space Wallpapers + + Here are my dark wallpapers from space. + ``` + - Or add tags (comma separated) in square brackets at the end of the file or folder name. Examples: + ``` + Wallpapers [Dark, Space] (will probably be deprecated) + Wallpapers [Abstract, Dark] (will probably be deprecated) + IMG001 [2021, Berlin].jpg + IMG002 [2022, New York].jpg + ``` Right-click tags in filter to exclude entries with the respective tag from the list. - Place an image file named like the folder to define a permanent cover/thumbnail. Example: - Folder name: `Wallpapers` diff --git a/package-lock.json b/package-lock.json index 3b69abe..15eed7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "highlight.js": "^11.4.0", "iconoir": "^1.0.0", "marked": "^4.0.12", + "ultramatter": "^0.0.4", "vue": "^3.2.41", "vue-router": "^4.1.5" }, @@ -8231,6 +8232,11 @@ "node": ">=4.2.0" } }, + "node_modules/ultramatter": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ultramatter/-/ultramatter-0.0.4.tgz", + "integrity": "sha512-1f/hO3mR+/Hgue4eInOF/Qm/wzDqwhYha4DxM0hre9YIUyso3fE2XtrAU6B4njLqTC8CM49EZaYgsVSa+dXHGw==" + }, "node_modules/unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", @@ -14967,6 +14973,11 @@ "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", "dev": true }, + "ultramatter": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ultramatter/-/ultramatter-0.0.4.tgz", + "integrity": "sha512-1f/hO3mR+/Hgue4eInOF/Qm/wzDqwhYha4DxM0hre9YIUyso3fE2XtrAU6B4njLqTC8CM49EZaYgsVSa+dXHGw==" + }, "unbox-primitive": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", diff --git a/package.json b/package.json index 7059982..7471883 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "highlight.js": "^11.4.0", "iconoir": "^1.0.0", "marked": "^4.0.12", + "ultramatter": "^0.0.4", "vue": "^3.2.41", "vue-router": "^4.1.5" }, diff --git a/src/features/useDir.ts b/src/features/useDir.ts index 614ddf4..e3ba839 100644 --- a/src/features/useDir.ts +++ b/src/features/useDir.ts @@ -1,3 +1,4 @@ +import { parse, type Result } from 'ultramatter'; import { computed, nextTick, onMounted, ref, watch } from 'vue'; import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'; import { getTagsFromString, removeTagsFromString } from '@/features/useDirFilter'; @@ -74,6 +75,19 @@ const currentDirEntriesFiles = computed(() => currentDirEntries.value.filter((e) export const currentDirEntriesLoading = ref(false); +export const resolveReadmeData = async ( + directoryHandle: FileSystemDirectoryHandle +): Promise => { + try { + const file = await directoryHandle.getFileHandle('README.md'); + const contents = await (await file.getFile()).text(); + + return parse(contents); + } catch (error) { + return { content: '' }; + } +}; + export const setupDirEntries = async (dir: FileSystemDirectoryHandle) => { const entries: CurrentDirEntry[] = []; @@ -103,10 +117,13 @@ export const setupDirEntries = async (dir: FileSystemDirectoryHandle) => { isFile: true, }); } else { + const { tags } = (await resolveReadmeData(entry)).frontmatter || {}; + const readmeTags = Array.isArray(tags) ? tags.map((t) => String(t)) : []; + entries.push({ handle: entry, displayName: removeTagsFromString(entry.name), - tags: getTagsFromString(entry.name), + tags: [...new Set([...readmeTags, ...getTagsFromString(entry.name)].sort())], file: false, type: 'dir', isActive: false, diff --git a/src/views/Dir.vue b/src/views/Dir.vue index c789b23..e98779b 100644 --- a/src/views/Dir.vue +++ b/src/views/Dir.vue @@ -9,6 +9,7 @@ import useDir, { currentDirEntriesLoading, getDirCoverEntry, reloadCurrentDir, + resolveReadmeData, } from '@/features/useDir'; import useDirFilter from '@/features/useDirFilter'; @@ -42,12 +43,8 @@ const toggleFilterHelper = async () => { $stringFilter.value?.focus(); }; -const readmeEntry = computed(() => - currentDirEntries.value.find( - (e) => e.type === 'markdown' && e.file.name.toLowerCase() === 'readme.md' - ) -); const readmeContent = ref(''); +const readmeTags = ref([]); const renderer = new marked.Renderer(); const linkRenderer = renderer.link; @@ -59,14 +56,25 @@ renderer.link = (href, title, text) => { : html.replace(/^ { - if (!entry || !entry.isFile) { - readmeContent.value = ''; - return; - } +watch( + currentDir, + async (dir) => { + if (!dir) { + readmeContent.value = ''; + return; + } + + const { content, frontmatter: { tags } = {} } = await resolveReadmeData(dir); - readmeContent.value = marked.parse(await entry.file.text(), { headerIds: false, renderer }); -}); + readmeContent.value = marked.parse(content, { headerIds: false, renderer }); + readmeTags.value = Array.isArray(tags) ? tags.map((t) => String(t)) : []; + }, + { immediate: true } +); + +const dirTags = computed(() => [ + ...new Set([...readmeTags.value, ...getTagsFromString(currentDir.value?.name || '')].sort()), +]); const dirThumbEntry = computed(() => getDirCoverEntry(currentDirEntries.value, removeTagsFromString(currentDir.value?.name || '')) @@ -83,10 +91,7 @@ const dirThumbSrc = computed( >
- +