Skip to content

Commit

Permalink
3.0.0 (#17)
Browse files Browse the repository at this point in the history
- Added setting to save base64-encoded images from clipboard when pasting. Thus, text like this:

```
One test
![Hello World](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAAUCAAAAAAVAxSkAAABrUlEQVQ4y+3TPUvDQBgH8OdDOGa+oUMgk2MpdHIIgpSUiqC0OKirgxYX8QVFRQRpBRF8KShqLbgIYkUEteCgFVuqUEVxEIkvJFhae3m8S2KbSkcFBw9yHP88+eXucgH8kQZ/jSm4VDaIy9RKCpKac9NKgU4uEJNwhHhK3qvPBVO8rxRWmFXPF+NSM1KVMbwriAMwhDgVcrxeMZm85GR0PhvGJAAmyozJsbsxgNEir4iEjIK0SYqGd8sOR3rJAGN2BCEkOxhxMhpd8Mk0CXtZacxi1hr20mI/rzgnxayoidevcGuHXTC/q6QuYSMt1jC+gBIiMg12v2vb5NlklChiWnhmFZpwvxDGzuUzV8kOg+N8UUvNBp64vy9q3UN7gDXhwWLY2nMC3zRDibfsY7wjEkY79CdMZhrxSqqzxf4ZRPXwzWJirMicDa5KwiPeARygHXKNMQHEy3rMopDR20XNZGbJzUtrwDC/KshlLDWyqdmhxZzCsdYmf2fWZPoxCEDyfIvdtNQH0PRkH6Q51g8rFO3Qzxh2LbItcDCOpmuOsV7ntNaERe3v/lP/zO8yn4N+yNPrekmPAAAAAElFTkSuQmCC)
Another test
```

   ... Will render with the image data replaced with a link to an automatically-saved image created from the data:

```
One test
![Hello World](Attachments/Pasted%20image%2020220424141143.png)
Another test
```

- Pasting images is now supported outside of Passthrough Mode.
  • Loading branch information
jglev authored Apr 24, 2022
1 parent 82fec85 commit b6fd41b
Show file tree
Hide file tree
Showing 7 changed files with 1,199 additions and 44 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
## Obsidian Paste to Current Indentation Plugin

_Note: Version 2.0.0+ is a rewrite of significant portions of this plugin and the way it works. If you were previously using a lower version, please be aware that Version 2 introduces the concept of "Paste Modes," explained below; and that it deprecates several Command Palette commands._

An [Obsidian](https://obsidian.md/) plugin to paste and manage text, including block quotes, that are indented. This plugin helps especially with embedding code, quotes, and other content within lists.

### "Paste modes"
Expand Down Expand Up @@ -33,6 +31,12 @@ There are multiple ways to switch paste modes:
1. Open the Command Palette and search for `Paste to Current Indentation: Cycle Paste Mode`. This command will cycle through the different paste modes (i.e., from Text mode to Text Blockquote mode, to Markdown mode, to Markdown Blockquote mode, to Passthrough mode, back to Text mode, etc.)
1. Within the plugin's Settings page.

### Limitations of Paste Modes

- The "Markdown" and "Markdown (Blockquote)" modes are automatically disabled **for one-time commands** (i.e., for the "Paste in Markdown Mode," "Paste in Markdown (Blockquote) Mode", and "Paste in Mode (Interactive)" commands) in Obsidian Mobile. This is due to how Obsidian Mobile is allowed to interact with the clipboard.
If these commands are disabled, a note stating such will appear in the Settings tab for the plugin.
- Similarly, in Obsidian Mobile, images such as screenshots, and similar files, cannot be pasted from the clipboard, due to how Obsidian Mobile is allowed to interact with the clipboard.

### Additional commands

- Within the Command Palette, the `Paste to Current Indentation: Toggle blockquote at current indentation` command will toggle blockquote markers at the highlighted text's current level of indentation.
Expand Down
172 changes: 162 additions & 10 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
addIcon,
App,
base64ToArrayBuffer,
Editor,
EditorTransaction,
FuzzySuggestModal,
Expand All @@ -10,11 +11,14 @@ import {
Platform,
Plugin,
PluginSettingTab,
TFile,
Setting,
} from "obsidian";

import { toggleQuote, toggleQuoteInEditor } from "./src/toggle-quote";

const moment = require("moment");

import * as pluginIcons from "./icons.json";

enum Mode {
Expand All @@ -27,6 +31,31 @@ enum Mode {
Passthrough = "Passthrough",
}

const createImageFileName = async (
fileLocation: string,
extension: string
): Promise<string> => {
let imageFileName = `${fileLocation || "."}/Pasted image ${moment().format(
"YYYYMMDDHHmmss"
)}.${extension}`;

// Address race condition whereby if multiple image files exist
// on the clipboard, they will all be saved to the same name:
let imageFileNameIndex = 0;
let imageFileNameWithIndex = imageFileName;
while (await app.vault.adapter.exists(imageFileNameWithIndex)) {
imageFileNameWithIndex = `${
fileLocation || "."
}/Pasted image ${moment().format(
"YYYYMMDDHHmmss"
)}_${imageFileNameIndex}.${extension}`;
imageFileNameIndex += 1;
}
imageFileName = imageFileNameWithIndex;

return imageFileName;
};

class PasteModeModal extends FuzzySuggestModal<number> {
public readonly onChooseItem: (item: number) => void;
public readonly currentValue: Mode;
Expand Down Expand Up @@ -104,13 +133,17 @@ class PasteModeModal extends FuzzySuggestModal<number> {
interface PastetoIndentationPluginSettings {
blockquotePrefix: string;
mode: Mode;
saveBase64EncodedFiles: boolean;
saveFilesLocation: string;
apiVersion: number;
}

const DEFAULT_SETTINGS: PastetoIndentationPluginSettings = {
blockquotePrefix: "> ",
mode: Mode.Markdown,
apiVersion: 2,
saveBase64EncodedFiles: false,
saveFilesLocation: "",
apiVersion: 3,
};

for (const [key, value] of Object.entries(pluginIcons)) {
Expand Down Expand Up @@ -145,9 +178,6 @@ export default class PastetoIndentationPlugin extends Plugin {
if (evt.defaultPrevented) {
return;
}
if (evt.clipboardData.types.every((type) => type === "files")) {
return;
}

let mode = this.settings.mode;

Expand All @@ -160,10 +190,52 @@ export default class PastetoIndentationPlugin extends Plugin {
let clipboardContents = "";
let output = "";

if (mode === Mode.Markdown || mode === Mode.MarkdownBlockquote) {
clipboardContents = htmlToMarkdown(
evt.clipboardData.getData("text/html")
// TODO: Add setting here.
// if (evt.clipboardData.types.every((type) => type === "files")) {
// return;
// }
const files = evt.clipboardData.files;
const fileLinks = [];
if (files.length) {
if (
!(await app.vault.adapter.exists(this.settings.saveFilesLocation))
) {
await app.vault.createFolder(this.settings.saveFilesLocation);
}
}

for (var i = 0; i < files.length; i++) {
const fileObject = files[i];

const fileName = await createImageFileName(
this.settings.saveFilesLocation,
fileObject.type.split("/")[1]
);

await app.vault.adapter.writeBinary(
fileName,
await fileObject.arrayBuffer()
);

const tfileObject = this.app.vault.getFiles().filter((f) => {
return f.path === fileName;
})[0];

if (tfileObject === undefined) {
continue;
}

const link = this.app.fileManager.generateMarkdownLink(
tfileObject,
this.app.workspace.getActiveFile().path
);

fileLinks.push(link);
}

if (mode === Mode.Markdown || mode === Mode.MarkdownBlockquote) {
const clipboardHtml = evt.clipboardData.getData("text/html");
clipboardContents = htmlToMarkdown(clipboardHtml);
// htmlToMarkdown() will return a blank string if
// there is no HTML to convert. If that is the case,
// we will switch to the equivalent Text mode:
Expand Down Expand Up @@ -192,15 +264,61 @@ export default class PastetoIndentationPlugin extends Plugin {
const leadingWhitespace =
leadingWhitespaceMatch !== null ? leadingWhitespaceMatch[1] : "";

let input = clipboardContents.split("\n").map((line, i) => {
if (
this.settings.saveBase64EncodedFiles &&
mode !== Mode.CodeBlock &&
mode !== Mode.CodeBlockBlockquote
) {
const images = [
...clipboardContents.matchAll(
/data:image\/(?<extension>.*?);base64,\s*(?<data>.*)\b/g
),
];

// We reverse images here in order that string
// changes not affect the accuracy of later images'
// indexes:
for (let image of images.reverse()) {
const imageFileName = await createImageFileName(
this.settings.saveFilesLocation,
image.groups.extension
);

if (
!(await app.vault.adapter.exists(this.settings.saveFilesLocation))
) {
await app.vault.createFolder(this.settings.saveFilesLocation);
}

await app.vault.adapter.writeBinary(
imageFileName,
base64ToArrayBuffer(image.groups.data)
);

clipboardContents =
clipboardContents.substring(0, image.index) +
`${encodeURI(imageFileName)}` +
clipboardContents.substring(
image.index + image[0].length,
clipboardContents.length
);
}
}

let input = [
...(clipboardContents.split("\n").join("") !== ""
? clipboardContents.split("\n")
: []),
...fileLinks,
].map((line, i) => {
if (i === 0) {
return line;
}
return leadingWhitespace + line;
});

if (mode === Mode.Text || mode === Mode.Markdown) {
output = input.join("\n");
output = output + input.join("\n");
}

if (mode === Mode.CodeBlock) {
Expand Down Expand Up @@ -381,7 +499,7 @@ export default class PastetoIndentationPlugin extends Plugin {
this.addCommand({
id: "paste-in-mode-interactive",
icon: "pasteIcons-clipboard-question",
name: "Paste in mode (Interactive)",
name: "Paste in Mode (Interactive)",
editorCallback: async (editor: Editor, view: MarkdownView) => {
const newMode = new PasteModeModal({
app,
Expand Down Expand Up @@ -480,6 +598,40 @@ class SettingTab extends PluginSettingTab {
})
);

new Setting(containerEl)
.setName("Save base64-encoded files")
.setDesc(
"When pasting in Text, Text (Blockquote), Markdown, or Markdown (Blockquote) mode, save any base64-encoded text as a file, and replace it in the pasted text with a reference to that saved file."
)
.addToggle((toggle) => {
toggle
.setValue(
this.plugin.settings.saveBase64EncodedFiles ||
DEFAULT_SETTINGS.saveBase64EncodedFiles
)
.onChange(async (value) => {
this.plugin.settings.saveBase64EncodedFiles = value;
await this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("base64-encoded file location")
.setDesc(
`When saving files from the clipboard, place them in this folder.`
)
.addText((text) => {
text
.setValue(
this.plugin.settings.saveFilesLocation ||
DEFAULT_SETTINGS.saveFilesLocation
)
.onChange(async (value) => {
this.plugin.settings.saveFilesLocation = value;
await this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("Blockquote Prefix")
.setDesc(
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"id": "obsidian-paste-to-current-indentation",
"name": "Paste to Current Indentation",
"version": "2.2.1",
"minAppVersion": "0.14.2",
"version": "3.0.0",
"minAppVersion": "0.14.6",
"description": "This plugin allows pasting and marking text as block-quotes at any level of indentation.",
"author": "Jacob Levernier",
"authorUrl": "https://github.com/jglev/obsidian-paste-to-current-indentation",
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-paste-to-current-indentation",
"version": "2.2.1",
"version": "3.0.0",
"description": "This plugin allows pasting and marking text as block-quotes at any level of indentation.",
"main": "main.js",
"scripts": {
Expand All @@ -16,11 +16,14 @@
"@types/jest": "^26.0.24",
"@types/node": "^14.14.37",
"esbuild": "0.13.12",
"obsidian": "^0.12.0",
"eslint-config-next": "^12.1.5",
"obsidian": "^0.14.6",
"tslib": "^2.2.0",
"tslint": "^6.1.3",
"typescript": "^4.3.5",
"xmldom": "^0.6.0"
},
"dependencies": {}
"dependencies": {
"moment": "^2.29.3"
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dom",
"es5",
"scripthost",
"es2015",
"es2020",
"ESNext.String"
]
},
Expand Down
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"2.1.0": "0.13.9",
"2.1.1": "0.13.9",
"2.2.0": "0.14.2",
"2.2.1": "0.14.2"
"2.2.1": "0.14.2",
"3.0.0": "0.14.6"
}
Loading

0 comments on commit b6fd41b

Please sign in to comment.