mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-14 03:21:17 +00:00
11th March, 2026
Now, Self-hosted LiveSync has finally begun to be split into the Self-hosted LiveSync plugin for Obsidian, and a properly abstracted version of it. This may not offer much benefit to Obsidian plugin users, or might even cause a slight inconvenience, but I believe it will certainly help improve testability and make the ecosystem better. However, I do not see the point in putting something with little benefit into beta, so I am handling this on the alpha branch. I would actually preferred to create an R&D branch, but I was not keen on the ampersand, and I feel it will eventually become a proper beta anyway. ### Refactored - Separated `ObsidianLiveSyncPlugin` into `ObsidianLiveSyncPlugin` and `LiveSyncBaseCore`. - Now `LiveSyncCore` indicates the type specified version of `LiveSyncBaseCore`. - Referencing `plugin.xxx` has been rewritten to referencing the corresponding service or `core.xxx`. ### Internal API changes - Storage Access APIs are now yielding Promises. This is to allow more limited storage platforms to be supported. ### R&D - Browser-version of Self-hosted LiveSync is now in development. This is not intended for public use now, but I will eventually make it available for testing. - We can see the code in `src/apps/webapp` for the browser version.
This commit is contained in:
123
src/apps/webapp/adapters/FSAPIVaultAdapter.ts
Normal file
123
src/apps/webapp/adapters/FSAPIVaultAdapter.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { FilePath, UXDataWriteOptions } from "../../../lib/src/common/types";
|
||||
import type { IVaultAdapter } from "../../../lib/src/serviceModules/adapters";
|
||||
import type { FSAPIFile, FSAPIFolder } from "./FSAPITypes";
|
||||
|
||||
/**
|
||||
* Vault adapter implementation for FileSystem API
|
||||
*/
|
||||
export class FSAPIVaultAdapter implements IVaultAdapter<FSAPIFile> {
|
||||
constructor(private rootHandle: FileSystemDirectoryHandle) {}
|
||||
|
||||
async read(file: FSAPIFile): Promise<string> {
|
||||
const fileObject = await file.handle.getFile();
|
||||
return await fileObject.text();
|
||||
}
|
||||
|
||||
async cachedRead(file: FSAPIFile): Promise<string> {
|
||||
// No caching in webapp version, just read directly
|
||||
return await this.read(file);
|
||||
}
|
||||
|
||||
async readBinary(file: FSAPIFile): Promise<ArrayBuffer> {
|
||||
const fileObject = await file.handle.getFile();
|
||||
return await fileObject.arrayBuffer();
|
||||
}
|
||||
|
||||
async modify(file: FSAPIFile, data: string, options?: UXDataWriteOptions): Promise<void> {
|
||||
const writable = await file.handle.createWritable();
|
||||
await writable.write(data);
|
||||
await writable.close();
|
||||
}
|
||||
|
||||
async modifyBinary(file: FSAPIFile, data: ArrayBuffer, options?: UXDataWriteOptions): Promise<void> {
|
||||
const writable = await file.handle.createWritable();
|
||||
await writable.write(data);
|
||||
await writable.close();
|
||||
}
|
||||
|
||||
async create(p: string, data: string, options?: UXDataWriteOptions): Promise<FSAPIFile> {
|
||||
const parts = p.split("/").filter((part) => part !== "");
|
||||
const fileName = parts[parts.length - 1];
|
||||
|
||||
// Navigate to parent directory, creating as needed
|
||||
let currentHandle = this.rootHandle;
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
currentHandle = await currentHandle.getDirectoryHandle(parts[i], { create: true });
|
||||
}
|
||||
|
||||
// Create the file
|
||||
const fileHandle = await currentHandle.getFileHandle(fileName, { create: true });
|
||||
const writable = await fileHandle.createWritable();
|
||||
await writable.write(data);
|
||||
await writable.close();
|
||||
|
||||
// Get file metadata
|
||||
const fileObject = await fileHandle.getFile();
|
||||
|
||||
return {
|
||||
path: p as FilePath,
|
||||
stat: {
|
||||
size: fileObject.size,
|
||||
mtime: fileObject.lastModified,
|
||||
ctime: fileObject.lastModified,
|
||||
type: "file",
|
||||
},
|
||||
handle: fileHandle,
|
||||
};
|
||||
}
|
||||
|
||||
async createBinary(p: string, data: ArrayBuffer, options?: UXDataWriteOptions): Promise<FSAPIFile> {
|
||||
const parts = p.split("/").filter((part) => part !== "");
|
||||
const fileName = parts[parts.length - 1];
|
||||
|
||||
// Navigate to parent directory, creating as needed
|
||||
let currentHandle = this.rootHandle;
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
currentHandle = await currentHandle.getDirectoryHandle(parts[i], { create: true });
|
||||
}
|
||||
|
||||
// Create the file
|
||||
const fileHandle = await currentHandle.getFileHandle(fileName, { create: true });
|
||||
const writable = await fileHandle.createWritable();
|
||||
await writable.write(data);
|
||||
await writable.close();
|
||||
|
||||
// Get file metadata
|
||||
const fileObject = await fileHandle.getFile();
|
||||
|
||||
return {
|
||||
path: p as FilePath,
|
||||
stat: {
|
||||
size: fileObject.size,
|
||||
mtime: fileObject.lastModified,
|
||||
ctime: fileObject.lastModified,
|
||||
type: "file",
|
||||
},
|
||||
handle: fileHandle,
|
||||
};
|
||||
}
|
||||
|
||||
async delete(file: FSAPIFile | FSAPIFolder, force = false): Promise<void> {
|
||||
const parts = file.path.split("/").filter((part) => part !== "");
|
||||
const name = parts[parts.length - 1];
|
||||
|
||||
// Navigate to parent directory
|
||||
let currentHandle = this.rootHandle;
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
currentHandle = await currentHandle.getDirectoryHandle(parts[i]);
|
||||
}
|
||||
|
||||
// Remove the entry
|
||||
await currentHandle.removeEntry(name, { recursive: force });
|
||||
}
|
||||
|
||||
async trash(file: FSAPIFile | FSAPIFolder, force = false): Promise<void> {
|
||||
// In webapp, trash is the same as delete (no recycle bin)
|
||||
await this.delete(file, force);
|
||||
}
|
||||
|
||||
trigger(name: string, ...data: any[]): any {
|
||||
// No-op in webapp version (no event system yet)
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user