feat: add export to text (#110)

This commit is contained in:
bingryan 2025-09-07 13:58:43 +08:00 committed by GitHub
parent dece75da08
commit 595eb1f482
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 274 additions and 3 deletions

View file

@ -15,6 +15,7 @@ export const OUTGOING_LINK_REGEXP = /(?<!!)\[\[(.*?)\]\]/g;
export enum OUTPUT_FORMATS {
MD = "Markdown",
HTML = "HTML",
TEXT = "Text",
}
export interface MarkdownExportPluginSettings {
@ -30,6 +31,10 @@ export interface MarkdownExportPluginSettings {
relAttachPath: boolean;
convertWikiLinksToMarkdown: boolean;
removeYamlHeader: boolean;
// Text export settings
textExportBulletPointMap: Record<number, string>;
textExportCheckboxUnchecked: string;
textExportCheckboxChecked: string;
}
export const DEFAULT_SETTINGS: MarkdownExportPluginSettings = {
@ -45,4 +50,14 @@ export const DEFAULT_SETTINGS: MarkdownExportPluginSettings = {
relAttachPath: true,
convertWikiLinksToMarkdown: false,
removeYamlHeader: false,
// Text export settings
textExportBulletPointMap: {
0: "●",
4: "○",
8: "■",
12: "►",
16: "•"
},
textExportCheckboxUnchecked: "☐",
textExportCheckboxChecked: "☑"
};

View file

@ -46,7 +46,11 @@ export default class MarkdownExportPlugin extends Plugin {
})
);
for (const outputFormat of [OUTPUT_FORMATS.MD, OUTPUT_FORMATS.HTML]) {
for (const outputFormat of [
OUTPUT_FORMATS.MD,
OUTPUT_FORMATS.HTML,
OUTPUT_FORMATS.TEXT,
]) {
this.addCommand({
id: "export-to-" + outputFormat,
name: `Export to ${outputFormat}`,
@ -63,7 +67,11 @@ export default class MarkdownExportPlugin extends Plugin {
}
registerDirMenu(menu: Menu, file: TAbstractFile) {
for (const outputFormat of [OUTPUT_FORMATS.MD, OUTPUT_FORMATS.HTML]) {
for (const outputFormat of [
OUTPUT_FORMATS.MD,
OUTPUT_FORMATS.HTML,
OUTPUT_FORMATS.TEXT,
]) {
const addMenuItem = (item: MenuItem) => {
item.setTitle(`Export to ${outputFormat}`);
item.onClick(async () => {
@ -127,7 +135,13 @@ class MarkdownExportSettingTab extends PluginSettingTab {
containerEl.empty();
containerEl.createEl("h2", { text: "Markdown Export" });
containerEl.createEl("h1", { text: "Obsidian Markdown Export" });
containerEl.createEl("p", { text: "Created by " }).createEl("a", {
text: "bingryan 🤓",
href: "https://github.com/bingryan",
});
containerEl.createEl("h3", { text: "Baisc Setting" });
new Setting(containerEl)
.setName("Output Path")
@ -292,5 +306,136 @@ class MarkdownExportSettingTab extends PluginSettingTab {
await this.plugin.saveSettings();
})
);
containerEl.createEl("h3", { text: "Export Text Setting" });
// Bullet point mapping settings
containerEl.createEl("h6", { text: "Bullet Point Symbols" });
containerEl.createEl("p", {
text: "Configure symbols for different indentation levels of bullet points.",
});
new Setting(containerEl)
.setName("Level 0 Bullet Point")
.setDesc("Symbol for top-level bullet points")
.addText((text) =>
text
.setPlaceholder("●")
.setValue(
this.plugin.settings.textExportBulletPointMap[0] || "●"
)
.onChange(async (value) => {
this.plugin.settings.textExportBulletPointMap[0] =
value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Level 1 Bullet Point")
.setDesc("Symbol for first-level indented bullet points (4 spaces)")
.addText((text) =>
text
.setPlaceholder("○")
.setValue(
this.plugin.settings.textExportBulletPointMap[4] || "○"
)
.onChange(async (value) => {
this.plugin.settings.textExportBulletPointMap[4] =
value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Level 2 Bullet Point")
.setDesc(
"Symbol for second-level indented bullet points (8 spaces)"
)
.addText((text) =>
text
.setPlaceholder("■")
.setValue(
this.plugin.settings.textExportBulletPointMap[8] || "■"
)
.onChange(async (value) => {
this.plugin.settings.textExportBulletPointMap[8] =
value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Level 3 Bullet Point")
.setDesc(
"Symbol for third-level indented bullet points (12 spaces)"
)
.addText((text) =>
text
.setPlaceholder("►")
.setValue(
this.plugin.settings.textExportBulletPointMap[12] || "►"
)
.onChange(async (value) => {
this.plugin.settings.textExportBulletPointMap[12] =
value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Level 4 Bullet Point")
.setDesc(
"Symbol for fourth-level indented bullet points (16 spaces)"
)
.addText((text) =>
text
.setPlaceholder("•")
.setValue(
this.plugin.settings.textExportBulletPointMap[16] || "•"
)
.onChange(async (value) => {
this.plugin.settings.textExportBulletPointMap[16] =
value;
await this.plugin.saveSettings();
})
);
// Checkbox symbols settings
containerEl.createEl("h6", { text: "Checkbox Symbols" });
containerEl.createEl("p", {
text: "Configure symbols for checkboxes.",
});
new Setting(containerEl)
.setName("Unchecked Checkbox")
.setDesc("Symbol for unchecked checkboxes")
.addText((text) =>
text
.setPlaceholder("☐")
.setValue(
this.plugin.settings.textExportCheckboxUnchecked || "☐"
)
.onChange(async (value) => {
this.plugin.settings.textExportCheckboxUnchecked =
value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Checked Checkbox")
.setDesc("Symbol for checked checkboxes")
.addText((text) =>
text
.setPlaceholder("☑")
.setValue(
this.plugin.settings.textExportCheckboxChecked || "☑"
)
.onChange(async (value) => {
this.plugin.settings.textExportCheckboxChecked = value;
await this.plugin.saveSettings();
})
);
}
}

View file

@ -340,6 +340,102 @@ export async function getEmbedMap(
return embedMap;
}
// Convert Markdown to plain text with specific formatting
export function convertMarkdownToText(
plugin: MarkdownExportPlugin,
markdown: string
): string {
let text = markdown;
// Replace checkboxes
// - [ ] becomes ☐
// - [x] becomes ☑
text = text.replace(
/- \[ \]/g,
plugin.settings.textExportCheckboxUnchecked
);
text = text.replace(/- \[x\]/g, plugin.settings.textExportCheckboxChecked);
// Replace bullet points with specific characters
// - becomes ●
// - becomes ○
// - becomes ■
// - becomes ►
// - becomes •
const lines = text.split("\n");
const processedLines = lines.map((line) => {
// Count leading spaces or tabs to determine indentation level
const leadingWhitespace = line.match(/^(\s*)/)?.[0] || "";
// Replace bullet points based on indentation level
if (line.trim().startsWith("- ")) {
// Remove leading whitespace for processing
const trimmedLine = line.trim();
// First, normalize tabs to spaces (1 tab = 4 spaces)
const normalizedIndent = leadingWhitespace.replace(/\t/g, " ");
const indentationLevel = Math.floor(normalizedIndent.length / 4);
let bulletPointSymbol =
plugin.settings.textExportBulletPointMap[indentationLevel * 4];
// If no symbol is configured for this level, use the default mapping
if (!bulletPointSymbol) {
// Use the configured symbol for this indentation level, or fall back to defaults
switch (indentationLevel) {
case 0:
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[0] || "●";
break;
case 1:
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[4] || "○";
break;
case 2:
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[8] || "■";
break;
case 3:
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[12] || "►";
break;
case 4:
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[16] || "•";
break;
default: {
// For deeper levels, use the last configured symbol or default to "•"
const levels = Object.keys(
plugin.settings.textExportBulletPointMap
)
.map(Number)
.sort((a, b) => a - b);
if (levels.length > 0) {
const deepestLevel = levels[levels.length - 1];
bulletPointSymbol =
plugin.settings.textExportBulletPointMap[
deepestLevel
] || "•";
} else {
bulletPointSymbol = "•";
}
}
}
}
return (
leadingWhitespace +
bulletPointSymbol +
" " +
trimmedLine.slice(2)
);
}
return line;
});
return processedLines.join("\n");
}
export async function tryCopyMarkdownByRead(
plugin: MarkdownExportPlugin,
{ file, outputFormat, outputSubPath = "." }: CopyMarkdownOptions
@ -511,6 +607,21 @@ export async function tryCopyMarkdownByRead(
await tryCreate(plugin, targetFile, content);
break;
}
case OUTPUT_FORMATS.TEXT: {
let filename;
if (plugin.settings.customFileName) {
filename = plugin.settings.customFileName + ".md";
} else {
filename = file.name;
}
const targetFile = path.join(
outDir,
filename.replace(".md", ".txt")
);
const textContent = convertMarkdownToText(plugin, content);
await tryCreate(plugin, targetFile, textContent);
break;
}
}
});
} catch (error) {