update: rewrite the markdown export translator
fix: #28 Markdown convert bug
This commit is contained in:
parent
992923c3f9
commit
71b961512d
|
|
@ -34,10 +34,6 @@
|
|||
<checkbox id="__addonRef__-export-enablefile" checked="true" />
|
||||
<label value="&zotero.__addonRef__.export.file.enable.label;" />
|
||||
</row>
|
||||
<row>
|
||||
<checkbox id="__addonRef__-export-embedImage" checked="true" />
|
||||
<label value="&zotero.__addonRef__.export.image.enable.label;" />
|
||||
</row>
|
||||
</rows>
|
||||
</groupbox>
|
||||
<groupbox flex="1">
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
<!ENTITY zotero.__addonRef__.export.link.enable.label "Embed Linked Notes">
|
||||
<!ENTITY zotero.__addonRef__.export.note.enable.label "Export to new note">
|
||||
<!ENTITY zotero.__addonRef__.export.markdown.label "MarkDown Settings">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "Export to file(MarkDown/HTML/RDF)">
|
||||
<!ENTITY zotero.__addonRef__.export.image.enable.label "Embed Images in MarkDown format">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "Export to MarkDown File">
|
||||
<!ENTITY zotero.__addonRef__.export.richtext.label "RichText(MS Word) Settings">
|
||||
<!ENTITY zotero.__addonRef__.export.copy.enable.label "Export to clipboard">
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@
|
|||
<!ENTITY zotero.__addonRef__.export.link.enable.label "嵌入链接的子笔记">
|
||||
<!ENTITY zotero.__addonRef__.export.note.enable.label "导出到新笔记">
|
||||
<!ENTITY zotero.__addonRef__.export.markdown.label "MarkDown设置">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "导出为文件(MarkDown/HTML/RDF)">
|
||||
<!ENTITY zotero.__addonRef__.export.image.enable.label "嵌入MarkDown格式图片">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "导出为MarkDown文件">
|
||||
<!ENTITY zotero.__addonRef__.export.richtext.label "富文本(MS Word)设置">
|
||||
<!ENTITY zotero.__addonRef__.export.copy.enable.label "导出到剪贴板">
|
||||
|
||||
|
|
|
|||
5
build.js
5
build.js
|
|
@ -157,6 +157,11 @@ copyFileSync(
|
|||
path.join(buildDir, "addon/components/zotero-protocol-handler.js")
|
||||
);
|
||||
|
||||
copyFileSync(
|
||||
"src/Better Note Markdown.js",
|
||||
path.join(buildDir, "addon/chrome/content/translators/Better Note Markdown.js")
|
||||
);
|
||||
|
||||
compressing.zip.compressDir(
|
||||
path.join(buildDir, "addon"),
|
||||
path.join(buildDir, `${name}.xpi`),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -45,6 +45,9 @@ class AddonEvents extends AddonBase {
|
|||
public async onInit() {
|
||||
Zotero.debug("Knowledge4Zotero: init called");
|
||||
await Zotero.uiReadyPromise;
|
||||
// Init translator
|
||||
// await loadTranslator(TRANSLATOR_ID_BETTER_MARKDOWN);
|
||||
// Init UI
|
||||
this._Addon.views.addOpenWorkspaceButton();
|
||||
this._Addon.views.addNewKnowledgeButton();
|
||||
this.addEditorInstanceListener();
|
||||
|
|
@ -1039,7 +1042,6 @@ class AddonEvents extends AddonBase {
|
|||
await this._Addon.knowledge.exportNoteToFile(
|
||||
message.content.editorInstance._item,
|
||||
options.embedLink,
|
||||
options.embedImage,
|
||||
options.exportFile,
|
||||
options.exportNote,
|
||||
options.exportCopy
|
||||
|
|
|
|||
|
|
@ -31,14 +31,6 @@ class AddonExport extends AddonBase {
|
|||
) as XUL.Checkbox
|
||||
).checked = embedLink;
|
||||
}
|
||||
let embedImage = Zotero.Prefs.get("Knowledge4Zotero.embedImage");
|
||||
if (typeof embedImage !== "undefined") {
|
||||
(
|
||||
this._window.document.getElementById(
|
||||
"Knowledge4Zotero-export-embedImage"
|
||||
) as XUL.Checkbox
|
||||
).checked = embedImage;
|
||||
}
|
||||
let exportNote = Zotero.Prefs.get("Knowledge4Zotero.exportNote");
|
||||
if (typeof exportNote !== "undefined") {
|
||||
(
|
||||
|
|
@ -71,11 +63,6 @@ class AddonExport extends AddonBase {
|
|||
"Knowledge4Zotero-export-embedLink"
|
||||
) as XUL.Checkbox
|
||||
).checked;
|
||||
let embedImage = (
|
||||
this._window.document.getElementById(
|
||||
"Knowledge4Zotero-export-embedImage"
|
||||
) as XUL.Checkbox
|
||||
).checked;
|
||||
let exportNote = (
|
||||
this._window.document.getElementById(
|
||||
"Knowledge4Zotero-export-enablenote"
|
||||
|
|
@ -88,7 +75,6 @@ class AddonExport extends AddonBase {
|
|||
).checked;
|
||||
Zotero.Prefs.set("Knowledge4Zotero.exportFile", exportFile);
|
||||
Zotero.Prefs.set("Knowledge4Zotero.embedLink", embedLink);
|
||||
Zotero.Prefs.set("Knowledge4Zotero.embedImage", embedImage);
|
||||
Zotero.Prefs.set("Knowledge4Zotero.exportNote", exportNote);
|
||||
Zotero.Prefs.set("Knowledge4Zotero.exportCopy", exportCopy);
|
||||
Zotero.debug(this.io);
|
||||
|
|
@ -96,7 +82,6 @@ class AddonExport extends AddonBase {
|
|||
this.io.dataOut = {
|
||||
exportFile: exportFile,
|
||||
embedLink: embedLink,
|
||||
embedImage: embedImage,
|
||||
exportNote: exportNote,
|
||||
exportCopy: exportCopy,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
const TRANSLATOR_ID_BETTER_MARKDOWN = "1412e9e2-51e1-42ec-aa35-e036a895534c";
|
||||
|
||||
const configs = {};
|
||||
|
||||
configs[TRANSLATOR_ID_BETTER_MARKDOWN] = {
|
||||
translatorID: TRANSLATOR_ID_BETTER_MARKDOWN,
|
||||
label: "Better Note Markdown",
|
||||
creator: "Martynas Bagdonas; Winding",
|
||||
target: "md",
|
||||
minVersion: "5.0.97",
|
||||
maxVersion: "",
|
||||
priority: 50,
|
||||
configOptions: {
|
||||
noteTranslator: true,
|
||||
},
|
||||
displayOptions: {
|
||||
includeAppLinks: true,
|
||||
},
|
||||
inRepository: true,
|
||||
translatorType: 2,
|
||||
lastUpdated: "2022-06-01 10:26:46",
|
||||
_codePath:
|
||||
"chrome://Knowledge4Zotero/content/translators/Better Note Markdown.js",
|
||||
};
|
||||
|
||||
async function loadTranslator(id) {
|
||||
const config = configs[id];
|
||||
const code = (await Zotero.File.getContentsAsync(config._codePath)).response;
|
||||
Zotero.debug(code);
|
||||
await Zotero.Translators.save(config, code);
|
||||
await Zotero.Translators.reinit();
|
||||
}
|
||||
|
||||
export { TRANSLATOR_ID_BETTER_MARKDOWN, loadTranslator };
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
export async function pick(title: string, mode: 'open' | 'save' | 'folder', filters?: [string, string][], suggestion?: string): Promise<string> {
|
||||
const fp = Components.classes['@mozilla.org/filepicker;1'].createInstance(Components.interfaces.nsIFilePicker)
|
||||
|
||||
if (suggestion) fp.defaultString = suggestion
|
||||
|
||||
mode = {
|
||||
open: Components.interfaces.nsIFilePicker.modeOpen,
|
||||
save: Components.interfaces.nsIFilePicker.modeSave,
|
||||
folder: Components.interfaces.nsIFilePicker.modeGetFolder,
|
||||
}[mode]
|
||||
|
||||
fp.init(window, title, mode)
|
||||
|
||||
for (const [label, ext] of (filters || [])) {
|
||||
fp.appendFilter(label, ext)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return new Zotero.Promise(resolve => {
|
||||
fp.open(userChoice => {
|
||||
switch (userChoice) {
|
||||
case Components.interfaces.nsIFilePicker.returnOK:
|
||||
case Components.interfaces.nsIFilePicker.returnReplace:
|
||||
resolve(fp.file.path)
|
||||
break
|
||||
|
||||
default: // aka returnCancel
|
||||
resolve('')
|
||||
break
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
202
src/knowledge.ts
202
src/knowledge.ts
|
|
@ -1,4 +1,6 @@
|
|||
import { AddonBase, EditorMessage, OutlineType } from "./base";
|
||||
import { loadTranslator, TRANSLATOR_ID_BETTER_MARKDOWN } from "./exportMD";
|
||||
import { pick } from "./file_picker";
|
||||
|
||||
const TreeModel = require("./treemodel");
|
||||
|
||||
|
|
@ -6,6 +8,8 @@ class Knowledge extends AddonBase {
|
|||
currentLine: number;
|
||||
currentNodeID: number;
|
||||
workspaceWindow: Window;
|
||||
_exportNote: ZoteroItem;
|
||||
_exportPath: string;
|
||||
constructor(parent: Knowledge4Zotero) {
|
||||
super(parent);
|
||||
this.currentLine = -1;
|
||||
|
|
@ -623,7 +627,6 @@ class Knowledge extends AddonBase {
|
|||
async exportNoteToFile(
|
||||
note: ZoteroItem,
|
||||
convertNoteLinks: boolean = true,
|
||||
convertNoteImages: boolean = true,
|
||||
saveFile: boolean = true,
|
||||
saveNote: boolean = false,
|
||||
saveCopy: boolean = false
|
||||
|
|
@ -632,29 +635,67 @@ class Knowledge extends AddonBase {
|
|||
return;
|
||||
}
|
||||
note = note || this.getWorkspaceNote();
|
||||
const noteID = await ZoteroPane_Local.newNote();
|
||||
const newNote = Zotero.Items.get(noteID);
|
||||
const rootNoteIds = [note.id];
|
||||
let newNote: ZoteroItem;
|
||||
if (convertNoteLinks || saveNote) {
|
||||
const noteID = await ZoteroPane_Local.newNote();
|
||||
newNote = Zotero.Items.get(noteID);
|
||||
const rootNoteIds = [note.id];
|
||||
|
||||
const convertResult = await this.convertNoteLines(
|
||||
note,
|
||||
rootNoteIds,
|
||||
convertNoteLinks,
|
||||
convertNoteImages
|
||||
);
|
||||
const convertResult = await this.convertNoteLines(
|
||||
note,
|
||||
rootNoteIds,
|
||||
convertNoteLinks
|
||||
);
|
||||
|
||||
this.setLinesToNote(newNote, convertResult.lines);
|
||||
Zotero.debug(convertResult.subNotes);
|
||||
this.setLinesToNote(newNote, convertResult.lines);
|
||||
Zotero.debug(convertResult.subNotes);
|
||||
|
||||
await Zotero.DB.executeTransaction(async () => {
|
||||
for (const subNote of convertResult.subNotes) {
|
||||
await Zotero.Notes.copyEmbeddedImages(subNote, newNote);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
newNote = note;
|
||||
}
|
||||
|
||||
await Zotero.DB.executeTransaction(async () => {
|
||||
for (const subNote of convertResult.subNotes) {
|
||||
await Zotero.Notes.copyEmbeddedImages(subNote, newNote);
|
||||
}
|
||||
});
|
||||
if (saveFile) {
|
||||
const exporter = new Zotero_File_Exporter();
|
||||
exporter.items = [newNote];
|
||||
await exporter.save();
|
||||
if (
|
||||
(await new Zotero.Translate.Export().getTranslators()).filter(
|
||||
(e) => e.translatorID === TRANSLATOR_ID_BETTER_MARKDOWN
|
||||
)
|
||||
) {
|
||||
await loadTranslator(TRANSLATOR_ID_BETTER_MARKDOWN);
|
||||
}
|
||||
|
||||
const filename = await pick(
|
||||
Zotero.getString("fileInterface.export"),
|
||||
"save",
|
||||
[["MarkDown File(*.md)", "*.md"]],
|
||||
`${newNote.getNoteTitle()}.md`
|
||||
);
|
||||
if (!filename) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._exportNote = newNote;
|
||||
this._exportPath =
|
||||
Zotero.File.pathToFile(filename).parent.path + "\\attachments";
|
||||
|
||||
const hasImage = newNote.getNote().includes("<img");
|
||||
if (hasImage) {
|
||||
await Zotero.File.createDirectoryIfMissingAsync(this._exportPath);
|
||||
}
|
||||
|
||||
const translator = new Zotero.Translate.Export();
|
||||
translator.setItems([newNote]);
|
||||
translator.setLocation(Zotero.File.pathToFile(filename));
|
||||
translator.setTranslator(TRANSLATOR_ID_BETTER_MARKDOWN);
|
||||
translator.translate();
|
||||
this._Addon.views.showProgressWindow(
|
||||
"Better Notes",
|
||||
`Note Saved to ${filename}`
|
||||
);
|
||||
}
|
||||
if (saveCopy) {
|
||||
if (!convertNoteLinks) {
|
||||
|
|
@ -674,115 +715,22 @@ class Knowledge extends AddonBase {
|
|||
}
|
||||
}
|
||||
if (!saveNote) {
|
||||
const _w: Window = ZoteroPane.findNoteWindow(newNote.id);
|
||||
if (_w) {
|
||||
_w.close();
|
||||
if (newNote.id !== note.id) {
|
||||
const _w: Window = ZoteroPane.findNoteWindow(newNote.id);
|
||||
if (_w) {
|
||||
_w.close();
|
||||
}
|
||||
await Zotero.Items.erase(newNote.id);
|
||||
}
|
||||
await Zotero.Items.erase(newNote.id);
|
||||
} else {
|
||||
ZoteroPane.openNoteWindow(newNote.id);
|
||||
}
|
||||
}
|
||||
|
||||
async convertImage(line: string, newLines: string[], sourceNote: ZoteroItem) {
|
||||
const imageReg = new RegExp("<img");
|
||||
const imageBrReg = new RegExp("<br>");
|
||||
const imageKeyReg = new RegExp(`data-attachment-key="`);
|
||||
const imageAnnotationReg = new RegExp(`data-annotation="`);
|
||||
|
||||
const imageIndex = line.search(imageReg);
|
||||
if (imageIndex !== -1) {
|
||||
const lineStart = line.slice(0, imageIndex);
|
||||
const imageLine = line.slice(imageIndex);
|
||||
const lineEnd = imageLine.slice(imageLine.search(imageBrReg));
|
||||
const attachmentKeyIndex = imageLine.search(imageKeyReg);
|
||||
const annotationIndex = imageLine.search(imageAnnotationReg);
|
||||
|
||||
if (attachmentKeyIndex !== -1) {
|
||||
let attachmentKey = imageLine.slice(
|
||||
attachmentKeyIndex + imageKeyReg.source.length
|
||||
);
|
||||
attachmentKey = attachmentKey.slice(0, attachmentKey.search(/"/g));
|
||||
const attachmentItem = await Zotero.Items.getByLibraryAndKeyAsync(
|
||||
sourceNote.libraryID,
|
||||
attachmentKey
|
||||
);
|
||||
let attachmentURL = await attachmentItem.getFilePathAsync();
|
||||
if (attachmentURL) {
|
||||
Zotero.debug("convert image");
|
||||
// const imageData = await editorInstance._getDataURL(
|
||||
// attachmentItem
|
||||
// );
|
||||
Zotero.debug(line);
|
||||
Zotero.debug(lineStart);
|
||||
Zotero.debug(lineEnd);
|
||||
if (Zotero.isMac) {
|
||||
attachmentURL = "file://" + attachmentURL;
|
||||
}
|
||||
newLines.push(`<p>!<a href="${attachmentURL}">image</a></p>`);
|
||||
|
||||
// Export annotation link
|
||||
if (annotationIndex !== -1) {
|
||||
let annotationContentRaw = imageLine.slice(
|
||||
annotationIndex + imageAnnotationReg.source.length
|
||||
);
|
||||
annotationContentRaw = annotationContentRaw.slice(
|
||||
0,
|
||||
annotationContentRaw.search('"')
|
||||
);
|
||||
if (annotationContentRaw) {
|
||||
Zotero.debug("convert image annotation");
|
||||
Zotero.debug(annotationContentRaw);
|
||||
try {
|
||||
let annotation = JSON.parse(
|
||||
decodeURIComponent(annotationContentRaw)
|
||||
);
|
||||
if (annotation) {
|
||||
// annotation.uri was used before note-editor v4
|
||||
let uri = annotation.attachmentURI || annotation.uri;
|
||||
let position = annotation.position;
|
||||
if (typeof uri === "string" && typeof position === "object") {
|
||||
let annotationURL;
|
||||
let uriParts = uri.split("/");
|
||||
let libraryType = uriParts[3];
|
||||
let key = uriParts[6];
|
||||
if (libraryType === "users") {
|
||||
annotationURL = "zotero://open-pdf/library/items/" + key;
|
||||
}
|
||||
// groups
|
||||
else {
|
||||
let groupID = uriParts[4];
|
||||
annotationURL =
|
||||
"zotero://open-pdf/groups/" + groupID + "/items/" + key;
|
||||
}
|
||||
|
||||
annotationURL +=
|
||||
"?page=" +
|
||||
(position.pageIndex + 1) +
|
||||
(annotation.annotationKey
|
||||
? "&annotation=" + annotation.annotationKey
|
||||
: "");
|
||||
newLines.push(`<p><a href="${annotationURL}">pdf</a></p>`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Zotero.debug(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
newLines.push(`${lineStart}${lineEnd}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async convertNoteLines(
|
||||
currentNote: ZoteroItem,
|
||||
rootNoteIds: number[],
|
||||
convertNoteLinks: boolean = true,
|
||||
convertNoteImages: boolean = true
|
||||
convertNoteLinks: boolean = true
|
||||
): Promise<{ lines: string[]; subNotes: ZoteroItem[] }> {
|
||||
Zotero.debug(`convert note ${currentNote.id}`);
|
||||
|
||||
|
|
@ -792,17 +740,6 @@ class Knowledge extends AddonBase {
|
|||
let newLines = [];
|
||||
const noteLines = this.getLinesInNote(currentNote);
|
||||
for (let i in noteLines) {
|
||||
// Embed Image
|
||||
if (convertNoteImages) {
|
||||
const hasImage = await this.convertImage(
|
||||
noteLines[i],
|
||||
newLines,
|
||||
currentNote
|
||||
);
|
||||
if (hasImage) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
newLines.push(noteLines[i]);
|
||||
// Convert Link
|
||||
if (convertNoteLinks) {
|
||||
|
|
@ -816,8 +753,7 @@ class Knowledge extends AddonBase {
|
|||
const convertResult = await this.convertNoteLines(
|
||||
subNote,
|
||||
_rootNoteIds,
|
||||
convertNoteLinks,
|
||||
convertNoteImages
|
||||
convertNoteLinks
|
||||
);
|
||||
const subNoteLines = convertResult.lines;
|
||||
let _newLine: string = "";
|
||||
|
|
|
|||
Loading…
Reference in New Issue