1
0
mirror of https://github.com/vrtmrz/obsidian-livesync.git synced 2024-12-03 08:45:34 +02:00

brushup readme

add delete handling
icon fix
This commit is contained in:
vorotamoroz 2021-10-13 21:38:44 +09:00
parent ed0d8372cb
commit 44efde7762
23 changed files with 563 additions and 217 deletions

106
README.md
View File

@ -1,24 +1,100 @@
# obsidian-livesync
This is the obsidian plugin that enables livesync between multi terminals.
This is the obsidian plugin that enables livesync between multi terminals.
<!-- screenshot/movie will coming. -->
**It's on the bleeding edge. Do not use for your "precious" Vault.**
**It's on the bleeding edge. Do not use for your "precious" Vault.**
## This plugin enables..
- Live sync
- Self-Hosted data synchronization with conflict detection and resolving in Obsidian.
## This plugin enables..
## how to use the experimental build
- Live sync
- Self-Hosted data synchronization with conflict detection and resolving in Obsidian.
1. download this repo and expand `[your-vault]/.obsidian/plugins/` (PC, Mac and Android will work)
1. enable obsidian livesync in the settings dialog.
1. If you use your self-hosted CouchDB, set your server's info.
1. or Use [IBM Cloudant](https://www.ibm.com/cloud/cloudant), take an account and enable **Cloudant** in [Catalog](https://cloud.ibm.com/catalog#services)
Note please choose "IAM and legacy credentials" for the Authentication method
Setup details are in Couldant Setup Section.
1. Setup LiveSync or SyncOnSave or SyncOnStart as you like.
## how to use the experimental build
### Cloudant Setup
**WIP**
1. download this repo and expand `[your-vault]/.obsidian/plugins/` (PC, Mac and Android will work)
1. enable obsidian livesync in the settings dialog.
1. If you use your self-hosted CouchDB, set your server's info.
1. or Use [IBM Cloudant](https://www.ibm.com/cloud/cloudant), take an account and enable **Cloudant** in [Catalog](https://cloud.ibm.com/catalog#services)
Note please choose "IAM and legacy credentials" for the Authentication method
Setup details are in Couldant Setup Section.
1. Setup LiveSync or SyncOnSave or SyncOnStart as you like.
## Cloudant Setup
### Creating an Instance
1. Hit the "Create Resource" button.
![step 1](./instruction_images/cloudant_1.png)
1. In IBM Cloud Catalog, search "Cloudant".
![step 2](instruction_images/cloudant_2.png)
1. You can choise "Lite plan" in free.
![step 3](instruction_images/cloudant_3.png)
Select Multitenant(it's the default) and the region as you like.
![step 4](instruction_images/cloudant_4.png) 3. Be sure to select "IAM and Legacy credentials" for "Authentication Method".
![step 5](instruction_images/cloudant_5.png)
4. Select Lite and be sure to check the capacity.
![step 6](instruction_images/cloudant_6.png)
5. And hit "Create" on the right panel.
![step 7](instruction_images/cloudant_7.png)
6. When all of the above steps have been done, Open "Resource list" on the left pane. you can see the Cloudant instance in the "Service and software". Click it.
![step 8](instruction_images/cloudant_8.png)
7. In resource details, there's information to connect from obsidian-livesync.
Copy the "External Endpoint(preferred)" address. <sup>(\*1)</sup>
![step 9](instruction_images/cloudant_9.png)
### CouchDB setup
1. Hit the "Launch Dashboard" button, Cloudant dashboard will be shown.
Yes, it's almost CouchDB's fauxton.
![step 1](instruction_images/couchdb_1.png)
1. First, you have to enable the CORS option.
Hit the Account menu and open the "CORS" tab.
Initially, "Origin Domains" is set to "Restrict to specific domains"., so set to "All domains(\*)"
_NOTE: of course We want to set "app://obsidian.md" but it's not acceptable on Cloudant._
![step 2](instruction_images/couchdb_2.png)
1. And open the "Databases" tab and hit the "Create Database" button.
Enter the name as you like <sup>(\*2)</sup> and Hit the "Create" button below.
![step 3](instruction_images/couchdb_3.png)
1. If the database was shown with joyful messages, then you can close this browser tab now.
![step 4](instruction_images/couchdb_4.png)
### Credentials Setup
1. Back into IBM Cloud, Open the "Service credentials". You'll get an empty list, hit the "New credential" button.
![step 1](instruction_images/credentials_1.png)
1. The dialog to create a credential will be shown.
type any name or leave it default, hit the "Add" button.
![step 2](instruction_images/credentials_2.png)
_NOTE: This "name" is not related to your username that uses in Obsidian-livesync._
1. Back to "Service credentials", the new credential should be created.
open details.
![step 3](instruction_images/credentials_3.png)
The username and password pair is inside this JSON.
"username" and "password" are so.
follow the figure, it's
"apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5"<sup>(\*3)</sup> and "c2c11651d75497fa3d3c486e4c8bdf27"<sup>(\*4)</sup>
### obsidian-livesync setting
![xx](instruction_images/obsidian_sync_1.png)
example values.
| Items | Value | example |
| ------------------- | ----------- | --------------------------------------------------------------------------- |
| CouchDB Remote URI: | (\*1)/(\*2) | https://xxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud/sync-test |
| CouchDB Username | (\*3) | apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5 |
| CouchDB Password | (\*4) | c2c11651d75497fa3d3c486e4c8bdf27 |

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

290
main.js

File diff suppressed because one or more lines are too long

300
main.ts
View File

@ -1,4 +1,4 @@
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, Vault, DataWriteOptions, View } from "obsidian";
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder } from "obsidian";
import { PouchDB } from "./pouchdb-browser-webpack/dist/pouchdb-browser";
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
@ -104,22 +104,47 @@ const escapeStringToHTML = (str: string) => {
return escape[match];
});
};
const isValidRemoteCouchDBURI = (uri: string): boolean => {
if (uri.startsWith("https://")) return true;
if (uri.startsWith("http://")) return true;
return false;
};
const connectRemoteCouchDB = async (uri: string, auth: { username: string; password: string }): Promise<false | { db: PouchDB.Database; info: any }> => {
if (!isValidRemoteCouchDBURI(uri)) false;
let db = new PouchDB(uri, {
auth,
});
try {
let info = await db.info();
return { db: db, info: info };
} catch (ex) {
return;
}
};
//<--Functions
export default class ObsidianLiveSyncPlugin extends Plugin {
settings: ObsidianLiveSyncSettings;
localDatabase: PouchDB.Database<Notes>;
logMessage: string[] = [];
onLogChanged: () => void;
async addLog(message: any) {
async addLog(message: any, isNotify?: boolean) {
let timestamp = new Date().toLocaleString();
let newmessage = timestamp + "->" + (typeof message == "string" ? message : JSON.stringify(message, null, 2));
let messagecontent = typeof message == "string" ? message : JSON.stringify(message, null, 2);
let newmessage = timestamp + "->" + messagecontent;
this.logMessage = [].concat(this.logMessage).concat([newmessage]).slice(-100);
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
console.log(newmessage);
if (this.statusBar2 != null) {
this.statusBar2.setText(newmessage);
this.statusBar2.setText(newmessage.substring(0, 60));
}
if (this.onLogChanged != null) {
this.onLogChanged();
}
if (isNotify) {
new Notice(messagecontent);
}
}
@ -132,8 +157,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
try {
await this.app.vault.createFolder(c);
} catch (ex) {
this.addLog("ensure excep");
this.addLog(ex);
// basically skip exceptions.
if (ex.message && ex.message == "Folder already exists.") {
// especialy this message is.
} else {
this.addLog("Folder Create Error");
this.addLog(ex);
}
}
c += "/";
}
@ -148,17 +178,27 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await this.app.vault.trigger("create", newfile);
}
}
async deleteVaultItem(file: TFile | TFolder) {
let dir = file.parent;
await this.app.vault.delete(file);
this.addLog(`deleted:${file.path}`);
this.addLog(`other items:${dir.children.length}`);
if (dir.children.length == 0) {
this.addLog(`all files deleted by replication, so delete dir`);
await this.deleteVaultItem(dir);
}
}
async doc2storate_modify(doc: Notes, file: TFile, force?: boolean) {
if (doc._deleted) {
//basically pass.
//but if there're no docs left, delete file.
try {
let lastDocs = this.localDatabase.get(doc._id);
this.addLog(`delete skipped:${doc._id}`);
let lastDocs = await this.localDatabase.get(doc._id);
this.addLog(`delete skipped:${lastDocs._id}`);
} catch (ex) {
if (ex.status || ex.status == 404) {
//no op.
await this.app.vault.delete(file);
await this.deleteVaultItem(file);
}
}
return;
@ -203,17 +243,17 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (this.syncHandler != null) {
return false;
}
let db = new PouchDB(this.settings.couchDB_URI, {
auth: {
username: this.settings.couchDB_USER,
password: this.settings.couchDB_PASSWORD,
},
let dbret = await connectRemoteCouchDB(this.settings.couchDB_URI, {
username: this.settings.couchDB_USER,
password: this.settings.couchDB_PASSWORD,
});
try {
let info = await db.info();
} catch (ex) {
if (dbret === false) {
this.addLog(`could not connect to ${this.settings.couchDB_URI}`, true);
return;
}
let db = dbret.db;
this.syncHandler = this.localDatabase.sync(db, { live: true, retry: true });
this.syncHandler
@ -222,20 +262,22 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
for (var change of docs) {
await this.pouchdbChanged(change as Notes);
}
this.addLog(`replicated ${docs.length} doc(s)`);
localStorage.setItem("last-sync-no", e.change.last_seq + "");
// new Notice(`changed:${JSON.stringify(e)}`);
})
.on("active", () => {
// new Notice(`Replication activated`);
this.addLog("Replication activated");
})
.on("complete", (e) => {
new Notice(`Replication completed`);
this.addLog("Replication completed", true);
})
.on("denied", (e) => {
new Notice(`Replication denied`);
this.addLog("Replication denied", true);
})
.on("error", (e) => {
new Notice(`Replication error`);
this.addLog("Replication error", true);
})
.on("paused", (e) => {
// new Notice(`Replication paused`);
@ -274,7 +316,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.statusBar.setText("Sync:" + statusStr);
}
async initializeDatabase() {
// debugger;
let vaultName = this.app.vault.getName();
this.localDatabase = new PouchDB<Notes>(vaultName + "-livesync", {
@ -289,14 +330,17 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
statusBar2: HTMLElement;
//<-- Sync
async replicate() {
let db = new PouchDB(this.settings.couchDB_URI, {
auth: {
username: this.settings.couchDB_USER,
password: this.settings.couchDB_PASSWORD,
},
let dbret = await connectRemoteCouchDB(this.settings.couchDB_URI, {
username: this.settings.couchDB_USER,
password: this.settings.couchDB_PASSWORD,
});
if (dbret === false) {
this.addLog("could not connect to database");
return;
}
try {
var info = await db.info();
let db = dbret.db;
var info = dbret.info;
new Notice(`Connected to ${info.db_name}`);
this.localDatabase
.sync(db, {
@ -304,8 +348,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
// since: "0",
})
.on("change", async (e) => {
// debugger;
let docs = e.change.docs;
for (var change of docs) {
this.addLog("replication change arrived");
@ -313,8 +355,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
await this.pouchdbChanged(change as Notes);
}
localStorage.setItem("last-sync-no", e.change.last_seq + "");
// this.addLog(e.change.last_seq);
// new Notice(`changed:${JSON.stringify(e)}`);
})
.on("active", () => {
new Notice(`Replication activated`);
@ -339,31 +379,47 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.addLog("loading plugin");
await this.loadSettings();
this.addRibbonIcon("dice", "Replicate", async () => {
addIcon(
"replicate",
`<g transform="matrix(1.15 0 0 1.15 -8.31 -9.52)" fill="currentColor" fill-rule="evenodd">
<path d="m85 22.2c-0.799-4.74-4.99-8.37-9.88-8.37-0.499 0-1.1 0.101-1.6 0.101-2.4-3.03-6.09-4.94-10.3-4.94-6.09 0-11.2 4.14-12.8 9.79-5.59 1.11-9.78 6.05-9.78 12 0 6.76 5.39 12.2 12 12.2h29.9c5.79 0 10.1-4.74 10.1-10.6 0-4.84-3.29-8.88-7.68-10.2zm-2.99 14.7h-29.5c-2.3-0.202-4.29-1.51-5.29-3.53-0.899-2.12-0.699-4.54 0.698-6.46 1.2-1.61 2.99-2.52 4.89-2.52 0.299 0 0.698 0 0.998 0.101l1.8 0.303v-2.02c0-3.63 2.4-6.76 5.89-7.57 0.599-0.101 1.2-0.202 1.8-0.202 2.89 0 5.49 1.62 6.79 4.24l0.598 1.21 1.3-0.504c0.599-0.202 1.3-0.303 2-0.303 1.3 0 2.5 0.404 3.59 1.11 1.6 1.21 2.6 3.13 2.6 5.15v1.61h2c2.6 0 4.69 2.12 4.69 4.74-0.099 2.52-2.2 4.64-4.79 4.64z"/>
<path d="m53.2 49.2h-41.6c-1.8 0-3.2 1.4-3.2 3.2v28.6c0 1.8 1.4 3.2 3.2 3.2h15.8v4h-7v6h24v-6h-7v-4h15.8c1.8 0 3.2-1.4 3.2-3.2v-28.6c0-1.8-1.4-3.2-3.2-3.2zm-2.8 29h-36v-23h36z"/>
<path d="m73 49.2c1.02 1.29 1.53 2.97 1.53 4.56 0 2.97-1.74 5.65-4.39 7.04v-4.06l-7.46 7.33 7.46 7.14v-4.06c7.66-1.98 12.2-9.61 10-17-0.102-0.297-0.205-0.595-0.307-0.892z"/>
<path d="m24.1 43c-0.817-0.991-1.53-2.97-1.53-4.56 0-2.97 1.74-5.65 4.39-7.04v4.06l7.46-7.33-7.46-7.14v4.06c-7.66 1.98-12.2 9.61-10 17 0.102 0.297 0.205 0.595 0.307 0.892z"/>
</g>`
);
addIcon(
"view-log",
`<g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd">
<path d="m103 330h76v12h-76z"/>
<path d="m106 346v44h70v-44zm45 16h-20v-8h20z"/>
</g>`
);
this.addRibbonIcon("replicate", "Replicate", async () => {
await this.replicate();
});
this.addRibbonIcon("dice", "pull file force", async () => {
this.localDatabase
.changes({
since: 0,
include_docs: true,
})
.on("change", async (change) => {
await this.pouchdbChanged(change.doc);
localStorage.setItem("last-sync-no", change.seq + "");
// received a change
})
.on("error", function (err) {
// handle errors
});
});
this.addRibbonIcon("dice", "Toggle Sync", () => {
this.toggleSync();
// Vault.recurseChildren();
// this.addRibbonIcon("dice", "pull file force", async () => {
// this.localDatabase
// .changes({
// since: 0,
// include_docs: true,
// })
// .on("change", async (change) => {
// await this.pouchdbChanged(change.doc);
// localStorage.setItem("last-sync-no", change.seq + "");
// // received a change
// })
// .on("error", function (err) {
// // handle errors
// });
// });
this.addRibbonIcon("view-log", "Show log", () => {
new LogDisplayModal(this.app, this).open();
});
this.statusBar = this.addStatusBarItem();
this.statusBar2 = this.addStatusBarItem();
this.watchVaultChange = debounce(this.watchVaultChange.bind(this), 200, false);
@ -423,17 +479,57 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
this.registerEvent(this.app.workspace.on("file-open", this.watchWorkspaceOpen));
}
watchWorkspaceOpen(file: TFile) {
if (file == null) return;
this.showIfConflicted(file);
}
watchVaultChange(file: TFile, ...args: any[]) {
this.updateDB(file);
}
watchVaultDelete(file: TFile) {
this.deleteDB(file);
watchVaultDelete(file: TFile & TFolder) {
// debugger;
if (file.children) {
//folder
this.deleteFolderOnDB(file);
// this.app.vault.delete(file);
} else {
this.deleteDB(file);
}
}
watchVaultRename(file: any, oldFile: any) {
this.updateDB(file);
this.deleteDBbyPath(oldFile);
async deleteFolderOnDB(folder: TFolder) {
this.addLog(`delete folder:${folder.path}`);
for (var v of folder.children) {
let entry = v as TFile & TFolder;
this.addLog(`->entry:${entry.path}`);
if (entry.children) {
this.addLog(`->is dir`);
await this.deleteFolderOnDB(entry);
await this.app.vault.delete(entry);
} else {
this.addLog(`->is file`);
await this.deleteDB(entry);
}
}
await this.app.vault.delete(folder);
}
watchVaultRename(file: TFile & TFolder, oldFile: any) {
if (file.children) {
// this.renameFolder(file,oldFile);
this.addLog(`folder name changed:(this operation is not supported) ${file.path}`);
} else {
this.updateDB(file);
this.deleteDBbyPath(oldFile);
}
}
async renameFolder(folder: TFolder, oldFile: any) {
for (var v of folder.children) {
let entry = v as TFile & TFolder;
if (entry.children) {
await this.deleteFolderOnDB(entry);
this.app.vault.delete(entry);
} else {
await this.deleteDB(entry);
}
}
}
// --> conflict resolving
@ -454,7 +550,18 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
return false;
}
async getConflictedStatus(path: string): Promise<diff_check_result> {
let test = await this.localDatabase.get(path, { conflicts: true });
let test: Notes & PouchDB.Core.GetMeta = null;
try {
test = await this.localDatabase.get(path, { conflicts: true });
} catch (ex) {
if (ex.status && ex.status == 404) {
this.addLog(`Getting conflicted status, but there was not ${path}`);
// NO OP.
} else {
throw ex;
}
}
if (test == null) return false;
if (!test._conflicts) return false;
if (test._conflicts.length == 0) return false;
// should be two or more conflicts;
@ -527,6 +634,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
let doc = await this.localDatabase.get(filename);
await this.doc2storate_modify(doc, file, force);
} else {
this.addLog(`target files:${filename} is two or more files in your vault`);
//something went wrong..
}
//when to opened file;
@ -559,8 +667,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
};
try {
let old = await this.localDatabase.get(fullpath);
let oldData = { data: old.data };
let newData = { data: d.data };
let oldData = { data: old.data, deleted: old._deleted };
let newData = { data: d.data, deleted: d._deleted };
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
this.addLog("no changed" + fullpath);
return;
@ -575,7 +683,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}
let ret = await this.localDatabase.put(d);
this.addLog("put database " + fullpath);
this.addLog("put database:" + fullpath);
this.addLog(ret);
if (this.settings.syncOnSave) {
await this.replicate();
@ -623,8 +731,36 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}
}
class LogDisplayModal extends Modal {
constructor(app: App) {
plugin: ObsidianLiveSyncPlugin;
logEl: HTMLDivElement;
constructor(app: App, plugin: ObsidianLiveSyncPlugin) {
super(app);
this.plugin = plugin;
}
updateLog() {
let logs = [...this.plugin.logMessage];
let msg = "";
for (var v of logs) {
msg += escapeStringToHTML(v) + "<br>";
}
this.logEl.innerHTML = msg;
}
onOpen() {
let { contentEl } = this;
contentEl.empty();
contentEl.createEl("h2", { text: "Sync Status" });
let div = contentEl.createDiv("");
div.addClass("op-scrollable");
div.addClass("op-pre");
this.logEl = div;
this.plugin.onLogChanged = this.updateLog;
this.updateLog();
}
onClose() {
let { contentEl } = this;
contentEl.empty();
this.plugin.onLogChanged = null;
}
}
class ConflictResolveModal extends Modal {
@ -700,18 +836,17 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
this.plugin = plugin;
}
async test(): Promise<void> {
let db = new PouchDB(this.plugin.settings.couchDB_URI, {
auth: {
username: this.plugin.settings.couchDB_USER,
password: this.plugin.settings.couchDB_PASSWORD,
},
let db = await connectRemoteCouchDB(this.plugin.settings.couchDB_URI, {
username: this.plugin.settings.couchDB_USER,
password: this.plugin.settings.couchDB_PASSWORD,
});
try {
var info = await db.info();
new Notice(`Connected to ${info.db_name}`);
} catch (ex) {
new Notice("Could not connect to db:" + ex);
if (db === false) {
this.plugin.addLog(`could not connect to ${this.plugin.settings.couchDB_URI}`);
new Notice(`could not connect to ${this.plugin.settings.couchDB_URI}`);
return;
}
new Notice(`Connected to ${db.info.db_name}`);
this.plugin.addLog(`Connected to ${db.info.db_name}`);
}
display(): void {
let { containerEl } = this;
@ -720,18 +855,15 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
containerEl.createEl("h2", { text: "Settings for obsidian-livesync." });
new Setting(containerEl)
.setName("CouchDB Remote URI")
.setDesc("It's a secret")
.addText((text) =>
text
.setPlaceholder("https://........")
.setValue(this.plugin.settings.couchDB_URI)
.onChange(async (value) => {
this.plugin.settings.couchDB_URI = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl).setName("CouchDB Remote URI").addText((text) =>
text
.setPlaceholder("https://........")
.setValue(this.plugin.settings.couchDB_URI)
.onChange(async (value) => {
this.plugin.settings.couchDB_URI = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("CouchDB Username")
.setDesc("username")
@ -794,7 +926,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
})
);
new Setting(containerEl)
.setName("Database Operations")
.setName("Local Database Operations")
.addButton((button) =>
button
.setButtonText("Reset local database")

View File

@ -1,40 +1,37 @@
import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import nodePolyfills from 'rollup-plugin-polyfill-node';
import typescript from "@rollup/plugin-typescript";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import nodePolyfills from "rollup-plugin-polyfill-node";
const isProd = (process.env.BUILD === 'production');
const isProd = process.env.BUILD === "production";
const banner =
`/*
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
`;
export default {
input: 'main.ts',
output: {
dir: '.',
sourcemap: 'inline',
sourcemapExcludeSources: isProd,
format: 'cjs',
exports: 'default',
banner,
},
// treeshake: "safest",
external: ['obsidian'],
plugins: [
typescript({exclude:["pouchdb-browser.js"]}),
nodePolyfills(
// // {crypto:true}
{ include: "pouchdb-browser" }
),
nodeResolve({
browser: true,
// preferBuiltins: true
}),
commonjs(),
]
input: "main.ts",
output: {
dir: ".",
sourcemap: "inline",
sourcemapExcludeSources: isProd,
format: "cjs",
exports: "default",
banner,
},
// treeshake: "safest",
external: ["obsidian"],
plugins: [
typescript({ exclude: ["pouchdb-browser.js", "pouchdb-browser-webpack"] }),
nodeResolve({
browser: true,
}),
commonjs(),
// nodePolyfills(
// // // {crypto:true}
// { include: "pouchdb-browser" }
// ),
],
};

View File

@ -1,22 +1,20 @@
/* Sets all the text color to red! */
/* body {
color: red;
} */
.added {
color:black;
color: black;
background-color: white;
}
.normal {
color:lightgray;
color: lightgray;
}
.deleted {
color:white;
.deleted {
color: white;
background-color: black;
/* text-decoration: line-through; */
}
.op-scrollable{
overflow-y:scroll;
.op-scrollable {
overflow-y: scroll;
/* min-height: 280px; */
max-height: 280px;
}
}
.op-pre {
white-space: pre-wrap;
}

View File

@ -12,5 +12,6 @@
"lib": ["dom", "es5", "scripthost", "es2015"]
},
"include": ["**/*.ts"],
"exclude": ["pouchdb-browser.js"]
"files": ["./main.ts"],
"exclude": ["pouchdb-browser-webpack"]
}