Table of Contents
- Introduction
- Screenshot
- Current Features
- Code
Introduction
UPDATE: This has been updated to build on top of File explorer from within Logseq
This is motivated by @alex0’s description #2 about reusing Logseq’s outliner for file management. This implementation is:
- Different on purpose:
- Forget most conveniences and prepare to learn a different workflow.
- It tries to blend with Logseq’s experience.
- Logseq is different and this is too.
- Experimental:
- No specific direction, other than proving the concept
- Highly UNTESTED
- If you want quality, use instead the file manager of your operating system.
- Missing important features. Here are only a few:
- No consideration for access rights
- No check for duplicate block entries
- NO UNDO
- Almost no safety:
- Not easy to make a mess, but certainly possible
- Use at own risk
- It is technically a SECURITY RISK
- Don’t use it in production
- Not easy to make a mess, but certainly possible
Screenshot
Current Features
- Every feature of File explorer from within Logseq
- Safely
click
on any button to see if that path currently exists in the file system.- Simple clicks make no changes.
Alt + click
proceeds to changes without confirmation.- Basic info is provided as messages and console logs.
Alt + click
on buttonDelete
to delete both the underlying file/folder and block.- Deleting manually a block or file/folder doesn’t update its pair.
- Subfolders and sub-blocks get also deleted.
Alt + click
on buttonCreate
to create a missing folder and all of its missing ancestors (if any) recursively.- This doesn’t create files.
Code
- Follow the steps at File explorer from within Logseq
- At the end of file
preload.js
, normally inside...\Logseq\app-0.9.13\resources\app\js
, replace the exposure ofgetfs
with the full set of functions like this:contextBridge.exposeInMainWorld('getfs', ()=>fs )
- The above is the security risk.
- Optional additional styles inside file
custom.css
:button.filesystem.create { background: #ffee00; } button.filesystem.delete { background: #ff4400; }
- Inside page
FileSystem
in Logseq, lower than the existing code-block, add the following code in a separate javascript code-block:
const LS = logseq.api
const Module = logseq.Module
const Block = Module.Block
function statusMsg(status, msg){
Module.Msg.ofStatus(msg, status)
}
const error = statusMsg.bind(null, "error")
const info = statusMsg.bind(null, "info")
const success = statusMsg.bind(null, "success")
const FS = Module.FileSystem
.setStatic(function appendButtonsForBlock(div, block){
const blockId = block.uuid
if (block.properties.foldername) div.append(
FS.button("Create", FS.onCreateClicked, blockId),
FS.button("Read", FS.onReadClicked, blockId)
)
div.append( FS.button("Delete", FS.onDeleteClicked, blockId) )
})
.setStatic(function create(block, cb){
const parent = Block.parentOf(block)
if (!parent) return cb("exists")
create(parent, (res)=>{
if (res !== "done" && res !== "exists") return cb(res)
const path = FS.fullPathOfBlock(block)
fs.access(path, (err)=>{
if (!err) return cb("exists")
fs.mkdir(path, (err)=>{
if (!err) console.log("CREATED FOLDER: " + path)
cb(err ? err.message : "done")
})
})
})
})
.setStatic(function onCreateClicked(blockId, e){
const block = LS.get_block(blockId)
fs.access(FS.fullPathOfBlock(block), (err)=>{
if (!err) return info("exists")
if (e.altKey) FS.create(block, FS.onCreated)
else info("Alt to create")
})
})
.setStatic(function onCreated(res){
if (res === "done") success("created")
else if (res === "exists") error("root should exist") // special
else error(res)
})
.setStatic(function onDeleteClicked(blockId, e){
const block = LS.get_block(blockId)
const fullPath = FS.fullPathOfBlock(block)
fs.access(fullPath, (err)=>{
if (err) return info("doesn't exist")
if (e.altKey) FS.unlink(fullPath, block)
else info("Alt to delete")
})
})
.setStatic(function onUnlinkFinished(type, path, block, err){
if (err) return error(FS.nameOfErr(err) + ": " + path)
console.log("DELETED " + type + ": " + path)
success("deleted")
LS.remove_block(block.uuid)
console.log("DELETED BLOCK: " + FS.nameOfBlock(block))
})
.setStatic(function unlink(path, block){
if (FS.isFolder(path)) {
const cb = FS.onUnlinkFinished.bind(null, "FOLDER", path, block)
fs.rmdir(path, {recursive: true, force: true}, cb)
} else {
const cb = FS.onUnlinkFinished.bind(null, "FILE", path, block)
fs.unlink(path, cb)
}
})
const fs = FS.fs