mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-19 04:31:29 +00:00
Implemented:
- Encrypting setup URI by passphrse. (Note: You have to make the setup URI again) Fixed: - Setup procedure fixed. - Status text fixed. - Documentation fixed.
This commit is contained in:
13
README.md
13
README.md
@@ -49,7 +49,7 @@ First, get your database ready. IBM Cloudant is preferred for testing. Or you ca
|
|||||||
|
|
||||||
1. Install the plugin on your device.
|
1. Install the plugin on your device.
|
||||||
2. Configure with the remote database.
|
2. Configure with the remote database.
|
||||||
1. Fill your server's information into the `Remote Database configuration pane`.
|
1. Fill your server's information into the `Remote Database configuration` pane.
|
||||||
2. Enabling `End to End Encryption` is recommended. After inputting the passphrase, you have to press `Just apply`.
|
2. Enabling `End to End Encryption` is recommended. After inputting the passphrase, you have to press `Just apply`.
|
||||||
3. Hit `Test Database Connection` and make sure that the plugin says `Connected`.
|
3. Hit `Test Database Connection` and make sure that the plugin says `Connected`.
|
||||||
4. Hit `Check database configuration` and make sure all tests have been passed.
|
4. Hit `Check database configuration` and make sure all tests have been passed.
|
||||||
@@ -63,27 +63,28 @@ First, get your database ready. IBM Cloudant is preferred for testing. Or you ca
|
|||||||
5. Back to the editor. I hope that initial scan is in the progress or done.
|
5. Back to the editor. I hope that initial scan is in the progress or done.
|
||||||
6. When status became stabilized (All ⏳ and 🧩 disappeared), you are ready to synchronize with the server.
|
6. When status became stabilized (All ⏳ and 🧩 disappeared), you are ready to synchronize with the server.
|
||||||
7. Press the replicate icon on the Ribbon or run `Replicate now` from the Command pallet. You'll send all your data to the server.
|
7. Press the replicate icon on the Ribbon or run `Replicate now` from the Command pallet. You'll send all your data to the server.
|
||||||
8. Open the command palette and run `Copy setup uri`. And share copied URI to your other devices.
|
8. Open the command palette, `Copy setup URI`, and set the passphrase to encrypt the information. Then your configuration will be copied to the clipboard. Please share copied URI with your other devices.
|
||||||
**IMPORTANT NOTICE: DO NOT EXPOSE THIS URI. THIS CONTAINS YOUR CREDENTIALS.**
|
**IMPORTANT NOTICE: BE CAREFUL TO TREAT THIS URI. THE URI CONTAINS YOUR CREDENTIALS EVEN THOUGH NOBODY COULD READ WITHOUT THE PASSPHRASE.**
|
||||||
|
|
||||||
### Subsequent Devices
|
### Subsequent Devices
|
||||||
|
|
||||||
Strongly recommend using the vault in which all files are completely synchronized including timestamps. Otherwise, some files will be corrupted if failed to resolve conflicts. To simplify, I recommend using a new empty vault.
|
Strongly recommend using the vault in which all files are completely synchronized including timestamps. Otherwise, some files will be corrupted if failed to resolve conflicts. To simplify, I recommend using a new empty vault.
|
||||||
|
|
||||||
1. Install the plug-in.
|
1. Install the plug-in.
|
||||||
2. Open the link that you had been copied to the other device.
|
2. Open the link that you copied from the other device.
|
||||||
|
1. If you are hard to open the link (i.e., in android), you can use `Open setup URI` from the command palette and paste the URI into LiveSync manually.
|
||||||
3. The plug-in asks you that are you sure to apply the configurations. Please answer `Yes` and the following instruction below:
|
3. The plug-in asks you that are you sure to apply the configurations. Please answer `Yes` and the following instruction below:
|
||||||
1. Answer `Yes` to `Keep local DB?`.
|
1. Answer `Yes` to `Keep local DB?`.
|
||||||
*Note: If you started with existed vault, you have to answer `No`. And `No` to `Rebuild the database?`.*
|
*Note: If you started with existed vault, you have to answer `No`. And `No` to `Rebuild the database?`.*
|
||||||
2. Answer `Yes` to `Keep remote DB?`.
|
2. Answer `Yes` to `Keep remote DB?`.
|
||||||
3. Answer `Yes` to `Replicate once?`.
|
3. Answer `Yes` to `Unlock and replicate?`.
|
||||||
Yes, you have to answer `Yes` to everything.
|
Yes, you have to answer `Yes` to everything.
|
||||||
Then, all your settings are copied from the first device.
|
Then, all your settings are copied from the first device.
|
||||||
4. Your notes will arrive soon.
|
4. Your notes will arrive soon.
|
||||||
|
|
||||||
## Something looks corrupted...
|
## Something looks corrupted...
|
||||||
|
|
||||||
Please open the link again and Answer as below:
|
Please open the link again and answer as below:
|
||||||
- If your local database looks corrupted
|
- If your local database looks corrupted
|
||||||
(in other words, when your Obsidian getting weird even standalone.)
|
(in other words, when your Obsidian getting weird even standalone.)
|
||||||
- Answer `No` to `Keep local DB?`
|
- Answer `No` to `Keep local DB?`
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.11.5",
|
"version": "0.11.6",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.11.5",
|
"version": "0.11.6",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.11.5",
|
"version": "0.11.6",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.11.5",
|
"version": "0.11.6",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
);
|
);
|
||||||
new Setting(containerRemoteDatabaseEl)
|
new Setting(containerRemoteDatabaseEl)
|
||||||
.setName("End to End Encryption")
|
.setName("End to End Encryption")
|
||||||
.setDesc("Encrypting contents on the remote database. If you use the plugins synchronizing feature, enabling this is recommend.")
|
.setDesc("Encrypt contents on the remote database. If you use the plugins synchronizing feature, enabling this is recommend.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.workingEncrypt).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.workingEncrypt).onChange(async (value) => {
|
||||||
this.plugin.settings.workingEncrypt = value;
|
this.plugin.settings.workingEncrypt = value;
|
||||||
|
|||||||
113
src/main.ts
113
src/main.ts
@@ -1,4 +1,4 @@
|
|||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest, Modal, App, FuzzySuggestModal } from "obsidian";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest, Modal, App, FuzzySuggestModal, Setting } from "obsidian";
|
||||||
import { diff_match_patch } from "diff-match-patch";
|
import { diff_match_patch } from "diff-match-patch";
|
||||||
|
|
||||||
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID } from "./lib/src/types";
|
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID } from "./lib/src/types";
|
||||||
@@ -59,6 +59,62 @@ class PluginDialogModal extends Modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InputStringDialog extends Modal {
|
||||||
|
result: string | false = false;
|
||||||
|
onSubmit: (result: string | boolean) => void;
|
||||||
|
title: string;
|
||||||
|
key: string;
|
||||||
|
placeholder: string;
|
||||||
|
isManuallyClosed = false;
|
||||||
|
|
||||||
|
constructor(app: App, title: string, key: string, placeholder: string, onSubmit: (result: string | false) => void) {
|
||||||
|
super(app);
|
||||||
|
this.onSubmit = onSubmit;
|
||||||
|
this.title = title;
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
|
||||||
|
contentEl.createEl("h1", { text: this.title });
|
||||||
|
|
||||||
|
new Setting(contentEl).setName(this.key).addText((text) =>
|
||||||
|
text.onChange((value) => {
|
||||||
|
this.result = value;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
new Setting(contentEl).addButton((btn) =>
|
||||||
|
btn
|
||||||
|
.setButtonText("Ok")
|
||||||
|
.setCta()
|
||||||
|
.onClick(() => {
|
||||||
|
this.isManuallyClosed = true;
|
||||||
|
this.close();
|
||||||
|
})
|
||||||
|
).addButton((btn) =>
|
||||||
|
btn
|
||||||
|
.setButtonText("Cancel")
|
||||||
|
.setCta()
|
||||||
|
.onClick(() => {
|
||||||
|
this.close();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
if (this.isManuallyClosed) {
|
||||||
|
this.onSubmit(this.result);
|
||||||
|
} else {
|
||||||
|
this.onSubmit(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
class PopoverYesNo extends FuzzySuggestModal<string> {
|
class PopoverYesNo extends FuzzySuggestModal<string> {
|
||||||
app: App;
|
app: App;
|
||||||
callback: (e: string) => void = () => { };
|
callback: (e: string) => void = () => { };
|
||||||
@@ -99,6 +155,13 @@ const askYesNo = (app: App, message: string): Promise<"yes" | "no"> => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const askString = (app: App, title: string, key: string, placeholder: string): Promise<string | false> => {
|
||||||
|
return new Promise((res) => {
|
||||||
|
const dialog = new InputStringDialog(app, title, key, placeholder, (result) => res(result));
|
||||||
|
dialog.open();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin {
|
export default class ObsidianLiveSyncPlugin extends Plugin {
|
||||||
settings: ObsidianLiveSyncSettings;
|
settings: ObsidianLiveSyncSettings;
|
||||||
localDatabase: LocalPouchDB;
|
localDatabase: LocalPouchDB;
|
||||||
@@ -241,20 +304,40 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger(ex, LOG_LEVEL.VERBOSE);
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const configURIBase = "obsidian://setuplivesync?settings=";
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-exportconfig",
|
id: "livesync-copysetupuri",
|
||||||
name: "Copy setup uri (beta)",
|
name: "Copy setup URI (beta)",
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
const encryptedSetting = encodeURIComponent(await encrypt(JSON.stringify(this.settings), "---"));
|
const encryptingPassphrase = await askString(this.app, "Encrypt your settings", "Passphrase", "");
|
||||||
const uri = `obsidian://setuplivesync?settings=${encryptedSetting}`;
|
if (encryptingPassphrase === false) return;
|
||||||
|
const encryptedSetting = encodeURIComponent(await encrypt(JSON.stringify(this.settings), encryptingPassphrase));
|
||||||
|
const uri = `${configURIBase}${encryptedSetting}`;
|
||||||
await navigator.clipboard.writeText(uri);
|
await navigator.clipboard.writeText(uri);
|
||||||
Logger("Setup uri copied to clipboard", LOG_LEVEL.NOTICE);
|
Logger("Setup URI copied to clipboard", LOG_LEVEL.NOTICE);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => {
|
this.addCommand({
|
||||||
|
id: "livesync-opensetupuri",
|
||||||
|
name: "Open setup URI (beta)",
|
||||||
|
callback: async () => {
|
||||||
|
const setupURI = await askString(this.app, "Set up manually", "Set up URI", `${configURIBase}aaaaa`);
|
||||||
|
if (setupURI === false) return;
|
||||||
|
if (!setupURI.startsWith(`${configURIBase}`)) {
|
||||||
|
Logger("Set up URI looks wrong.", LOG_LEVEL.NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const config = decodeURIComponent(setupURI.substring(configURIBase.length));
|
||||||
|
console.dir(config)
|
||||||
|
await setupwizard(config);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const setupwizard = async (confString: string) => {
|
||||||
try {
|
try {
|
||||||
const oldConf = JSON.parse(JSON.stringify(this.settings));
|
const oldConf = JSON.parse(JSON.stringify(this.settings));
|
||||||
const newconf = await JSON.parse(await decrypt(conf.settings, "---"));
|
const encryptingPassphrase = await askString(this.app, "Passphrase", "Passphrase for your settings", "");
|
||||||
|
if (encryptingPassphrase === false) return;
|
||||||
|
const newconf = await JSON.parse(await decrypt(confString, encryptingPassphrase));
|
||||||
if (newconf) {
|
if (newconf) {
|
||||||
const result = await askYesNo(this.app, "Importing LiveSync's conf, OK?");
|
const result = await askYesNo(this.app, "Importing LiveSync's conf, OK?");
|
||||||
if (result == "yes") {
|
if (result == "yes") {
|
||||||
@@ -269,6 +352,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
// nothing to do. so peaceful.
|
// nothing to do. so peaceful.
|
||||||
this.settings = newSettingW;
|
this.settings = newSettingW;
|
||||||
await this.saveSettings();
|
await this.saveSettings();
|
||||||
|
const replicate = await askYesNo(this.app, "Unlock and replicate?");
|
||||||
|
if (replicate == "yes") {
|
||||||
|
await this.replicate(true);
|
||||||
|
await this.markRemoteUnlocked();
|
||||||
|
}
|
||||||
Logger("Configuration loaded.", LOG_LEVEL.NOTICE);
|
Logger("Configuration loaded.", LOG_LEVEL.NOTICE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -313,8 +401,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger("Cancelled.", LOG_LEVEL.NOTICE);
|
Logger("Cancelled.", LOG_LEVEL.NOTICE);
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("Couldn't parse configuration uri.", LOG_LEVEL.NOTICE);
|
Logger("Couldn't parse or decrypt configuration uri.", LOG_LEVEL.NOTICE);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => {
|
||||||
|
await setupwizard(conf.settings);
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-replicate",
|
id: "livesync-replicate",
|
||||||
@@ -1217,14 +1308,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
const locks = getLocks();
|
const locks = getLocks();
|
||||||
const pendingTask = locks.pending.length
|
const pendingTask = locks.pending.length
|
||||||
? "\nPending: " +
|
? "\nPending: " +
|
||||||
Object.entries([...new Set([...locks.pending])].reduce((p, c) => ({ ...p, [c]: p[c] ?? 0 + 1 }), {} as { [key: string]: number }))
|
Object.entries(locks.pending.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const runningTask = locks.running.length
|
const runningTask = locks.running.length
|
||||||
? "\nRunning: " +
|
? "\nRunning: " +
|
||||||
Object.entries([...new Set([...locks.running])].reduce((p, c) => ({ ...p, [c]: p[c] ?? 0 + 1 }), {} as { [key: string]: number }))
|
Object.entries(locks.running.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
.join(", ")
|
.join(", ")
|
||||||
: "";
|
: "";
|
||||||
|
|||||||
41
styles.css
41
styles.css
@@ -2,28 +2,33 @@
|
|||||||
color: var(--text-on-accent);
|
color: var(--text-on-accent);
|
||||||
background-color: var(--text-accent);
|
background-color: var(--text-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.normal {
|
.normal {
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.deleted {
|
.deleted {
|
||||||
color: var(--text-on-accent);
|
color: var(--text-on-accent);
|
||||||
background-color: var(--text-muted);
|
background-color: var(--text-muted);
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-scrollable {
|
.op-scrollable {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
/* min-height: 280px; */
|
/* min-height: 280px; */
|
||||||
max-height: 280px;
|
max-height: 280px;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-pre {
|
.op-pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-warn {
|
.op-warn {
|
||||||
border: 1px solid salmon;
|
border: 1px solid salmon;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-warn::before {
|
.op-warn::before {
|
||||||
content: "Warning";
|
content: "Warning";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -31,11 +36,13 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-warn-info {
|
.op-warn-info {
|
||||||
border: 1px solid rgb(255, 209, 81);
|
border: 1px solid rgb(255, 209, 81);
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-warn-info::before {
|
.op-warn-info::before {
|
||||||
content: "Notice";
|
content: "Notice";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -43,27 +50,33 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.syncstatusbar {
|
.syncstatusbar {
|
||||||
-webkit-filter: grayscale(100%);
|
-webkit-filter: grayscale(100%);
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tcenter {
|
.tcenter {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-plugins-wrap {
|
.sls-plugins-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-plugins-tbl {
|
.sls-plugins-tbl {
|
||||||
border: 1px solid var(--background-modifier-border);
|
border: 1px solid var(--background-modifier-border);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider th {
|
.divider th {
|
||||||
border-top: 1px solid var(--background-modifier-border);
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* .sls-table-head{
|
/* .sls-table-head{
|
||||||
width:50%;
|
width:50%;
|
||||||
}
|
}
|
||||||
@@ -75,9 +88,11 @@
|
|||||||
.sls-btn-left {
|
.sls-btn-left {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-btn-right {
|
.sls-btn-right {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-hidden {
|
.sls-hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -85,8 +100,9 @@
|
|||||||
:root {
|
:root {
|
||||||
--slsmessage: "";
|
--slsmessage: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-wrap::before,
|
.CodeMirror-wrap::before,
|
||||||
.cm-s-obsidian > .cm-editor::before {
|
.cm-s-obsidian>.cm-editor::before {
|
||||||
content: var(--slsmessage);
|
content: var(--slsmessage);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -105,12 +121,15 @@
|
|||||||
.CodeMirror-wrap::before {
|
.CodeMirror-wrap::before {
|
||||||
right: 0px;
|
right: 0px;
|
||||||
}
|
}
|
||||||
.cm-s-obsidian > .cm-editor::before {
|
|
||||||
|
.cm-s-obsidian>.cm-editor::before {
|
||||||
right: 16px;
|
right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-setting-tab {
|
.sls-setting-tab {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.sls-setting-menu-btn {
|
div.sls-setting-menu-btn {
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
background-color: var(--background-secondary-alt);
|
background-color: var(--background-secondary-alt);
|
||||||
@@ -131,8 +150,9 @@ div.sls-setting-menu-btn {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
/* width: 100%; */
|
/* width: 100%; */
|
||||||
}
|
}
|
||||||
.sls-setting-tab:hover ~ div.sls-setting-menu-btn,
|
|
||||||
.sls-setting-tab:checked ~ div.sls-setting-menu-btn {
|
.sls-setting-tab:hover~div.sls-setting-menu-btn,
|
||||||
|
.sls-setting-tab:checked~div.sls-setting-menu-btn {
|
||||||
background-color: var(--interactive-accent);
|
background-color: var(--interactive-accent);
|
||||||
color: var(--text-on-accent);
|
color: var(--text-on-accent);
|
||||||
}
|
}
|
||||||
@@ -143,14 +163,17 @@ div.sls-setting-menu-btn {
|
|||||||
/* flex-wrap: wrap; */
|
/* flex-wrap: wrap; */
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-setting-label {
|
.sls-setting-label {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-collapsed {
|
.setting-collapsed {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-plugins-tbl-buttons {
|
.sls-plugins-tbl-buttons {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -159,13 +182,16 @@ div.sls-setting-menu-btn {
|
|||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sls-plugins-tbl-device-head {
|
.sls-plugins-tbl-device-head {
|
||||||
background-color: var(--background-secondary-alt);
|
background-color: var(--background-secondary-alt);
|
||||||
color: var(--text-accent);
|
color: var(--text-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-flex {
|
.op-flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.op-flex input {
|
.op-flex input {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@@ -185,9 +211,11 @@ div.sls-setting-menu-btn {
|
|||||||
color: var(--text-on-accent);
|
color: var(--text-on-accent);
|
||||||
background-color: var(--text-accent);
|
background-color: var(--text-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-normal {
|
.history-normal {
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.history-deleted {
|
.history-deleted {
|
||||||
color: var(--text-on-accent);
|
color: var(--text-on-accent);
|
||||||
background-color: var(--text-muted);
|
background-color: var(--text-muted);
|
||||||
@@ -197,6 +225,7 @@ div.sls-setting-menu-btn {
|
|||||||
.ob-btn-config-fix label {
|
.ob-btn-config-fix label {
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ob-btn-config-info {
|
.ob-btn-config-info {
|
||||||
border: 1px solid salmon;
|
border: 1px solid salmon;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
@@ -208,4 +237,4 @@ div.sls-setting-menu-btn {
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user