feat: support configurable block ID insertion position

This commit is contained in:
Moy 2025-11-05 13:52:26 +08:00
parent bf98aeaa09
commit a20a9911f9
4 changed files with 70 additions and 8 deletions

View file

@ -34,6 +34,8 @@ export type TranslationKey =
| 'callout-copy-priority' | 'callout-copy-priority-desc'
| 'auto-add-block-id' | 'auto-add-block-id-desc'
| 'manual-block-id' | 'manual-block-id-desc'
| 'block-id-insert-position' | 'block-id-insert-position-desc'
| 'block-id-end-of-block' | 'block-id-next-line' | 'block-id-next-line-with-gap'
| 'modal-block-id' | 'modal-block-id-desc'
| 'auto-block-display-text' | 'auto-block-display-text-desc'
| 'block-display-word-limit' | 'block-display-word-limit-desc'
@ -49,6 +51,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
'auto-add-block-id-desc': 'When enabled, if there is no copyable content, a random block ID (^xxxx) will be automatically added to the end of the current line.' ,
'manual-block-id': 'Manually enter Block ID',
'manual-block-id-desc': 'If enabled, you will be prompted to enter a block ID manually.',
'block-id-insert-position': 'Block ID insert position',
'block-id-insert-position-desc': 'Choose where to insert the block ID - For special blocks like code blocks and quote blocks, at least one line break will be inserted.',
'block-id-end-of-block': 'End of current block',
'block-id-next-line': 'Next line below current block',
'block-id-next-line-with-gap': 'Two lines below current block (with empty line)',
'modal-block-id': 'Enter block ID',
'modal-block-id-desc': 'Allowed: letters, numbers, hyphens (-), underscores (_). Spaces will be converted to hyphens.',
'error-block-id-empty': 'Block ID cannot be empty',
@ -144,6 +151,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
'auto-add-block-id-desc': '启用后如果没有可复制内容时会自动在当前文本末尾添加一个随机生成的块ID^xxxx',
'manual-block-id': '手动输入块ID',
'manual-block-id-desc': '启用后可以在弹窗中手动输入块ID',
'block-id-insert-position': '块ID插入位置',
'block-id-insert-position-desc': '选择生成块ID时的插入位置。对于代码块和引用块等特殊段落至少会插入一个换行。',
'block-id-end-of-block': '当前块的末尾',
'block-id-next-line': '当前块的下方一行',
'block-id-next-line-with-gap': '当前块的下方两行(中间隔一个空行)',
'modal-block-id': '输入块ID',
'modal-block-id-desc': '仅允许字母、数字、短横线(-、下划线_空格会自动转为短横线。',
'error-block-id-empty': '块ID不能为空',
@ -245,6 +257,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
'block-display-char-limit-desc': '非空格分隔語言(如中文 "這是一句話"在塊顯示文本中顯示的最大字符數——當第一行包含非ASCII字符時會採用此設置。',
'manual-block-id': '手動輸入塊ID',
'manual-block-id-desc': '啟用後可以在彈窗中手動輸入塊ID',
'block-id-insert-position': '塊ID插入位置',
'block-id-insert-position-desc': '選擇生成塊ID時的插入位置。對於代碼塊和引用塊等特殊段落至少會插入一個換行。',
'block-id-end-of-block': '當前塊的末尾',
'block-id-next-line': '當前塊的下方一行',
'block-id-next-line-with-gap': '當前塊的下方兩行(中間隔一個空行)',
'modal-block-id': '輸入塊ID',
'modal-block-id-desc': '僅允許字母、數字、連字號(-、底線_空格會自動轉為連字號。',
'error-block-id-empty': '塊ID 不能為空',

View file

@ -1,6 +1,6 @@
import { Editor, MarkdownView, Notice, Plugin, Menu, Platform, MarkdownFileInfo } from 'obsidian';
import { Language, TranslationKey, I18n } from './i18n';
import { ContextData, ContextType, DEFAULT_SETTINGS, EasyCopySettings, LinkFormat } from './type';
import { ContextData, ContextType, DEFAULT_SETTINGS, EasyCopySettings, LinkFormat, BlockIdInsertPosition } from './type';
import { EasyCopySettingTab } from './settingTab';
import { BlockIdInputModal } from './blockIdModal';
@ -134,7 +134,7 @@ export default class EasyCopy extends Plugin {
}
/**
* block ID
* 1~2 block ID
*/
private detectBlockRange(editor: Editor, cursorLine: number): { start: number, end: number } {
// 如果当前行是列表,那么范围就是当前行(不对,要继续延伸到后面的……只能说开头是定了)
@ -147,14 +147,17 @@ export default class EasyCopy extends Plugin {
}
const totalLines = editor.lineCount();
let start = cursorLine;
while (start > 0 && this.isContinuousText(editor.getLine(start - 1))) {
start--;
}
let end = cursorLine;
while (end < totalLines - 1 && this.isContinuousText(editor.getLine(end + 1))) {
end++;
}
return { start, end };
}
@ -261,7 +264,7 @@ export default class EasyCopy extends Plugin {
{ type: ContextType.HIGHLIGHT, regex: /==([^=]+)==/g , enable: !this.settings.customizeTargets || this.settings.enableHighlight},
{ type: ContextType.STRIKETHROUGH, regex: /~~([^~]+)~~/g , enable: !this.settings.customizeTargets || this.settings.enableStrikethrough},
{ type: ContextType.INLINECODE, regex: /`([^`]+)`/g , enable: !this.settings.customizeTargets || this.settings.enableInlineCode},
{ type: ContextType.INLINELATEX, regex: /\$([^$]*)\$/g , enable: !this.settings.customizeTargets || this.settings.enableInlineLatex},
{ type: ContextType.INLINELATEX, regex: /\$([^$]+)\$/g , enable: !this.settings.customizeTargets || this.settings.enableInlineLatex},
{ type: ContextType.WIKILINK, regex: /\[\[([^\]]+)\]\]/g, enable: !this.settings.customizeTargets || this.settings.enableWikiLink },
];
@ -745,11 +748,30 @@ export default class EasyCopy extends Plugin {
if (!/\^[a-zA-Z0-9_-]+$/.test(lastLine.trim())) {
// 在 block 最后一行末尾插入块ID
let insertText = '^' + blockId;
// 代码块、引用块、数学块等
const isSpecialBlock = lastLine.startsWith('> ') || lastLine.startsWith('```') || lastLine.startsWith('$$');
const needsSeparator = lastLine.trim().length > 0 && !lastLine.endsWith(' '); // 末尾需要空格分隔符
if (lastLine.startsWith('> ') || lastLine.startsWith('``')) {
insertText = '\n' + insertText;
} else if ( lastLine.trim().length > 0 && !lastLine.endsWith(' ')) {
insertText = ' ' + insertText;
// 添加 block id 到末尾——读取设置中的位置
const insertPosition = this.settings.blockIdInsertPosition;
if (isSpecialBlock) {
insertText = "\n" + insertText;
} else {
switch (insertPosition) {
case BlockIdInsertPosition.END_OF_BLOCK:
if (needsSeparator) {
insertText = ' ' + insertText;
}
break;
case BlockIdInsertPosition.NEXT_LINE:
insertText = "\n" + insertText;
break;
default:
insertText = "\n" + insertText;
break;
}
}
editor.replaceRange(insertText, { line: end, ch: lastLine.length });

View file

@ -1,6 +1,6 @@
import { App, PluginSettingTab, Setting } from "obsidian";
import EasyCopy from "./main";
import { LinkFormat } from "./type";
import { LinkFormat, BlockIdInsertPosition } from "./type";
export class EasyCopySettingTab extends PluginSettingTab {
@ -145,6 +145,21 @@ export class EasyCopySettingTab extends PluginSettingTab {
this.display();
}));
if (this.plugin.settings.autoAddBlockId) {
// 新增块ID插入位置设置
new Setting(containerEl)
.setName(this.plugin.t('block-id-insert-position'))
.setDesc(this.plugin.t('block-id-insert-position-desc'))
.addDropdown(dropdown => dropdown
.addOption(BlockIdInsertPosition.END_OF_BLOCK, this.plugin.t('block-id-end-of-block'))
.addOption(BlockIdInsertPosition.NEXT_LINE, this.plugin.t('block-id-next-line'))
.setValue(this.plugin.settings.blockIdInsertPosition)
.onChange(async (value) => {
this.plugin.settings.blockIdInsertPosition = value as BlockIdInsertPosition;
await this.plugin.saveSettings();
}));
}
if (this.plugin.settings.autoAddBlockId) {
new Setting(containerEl)
.setName(this.plugin.t('manual-block-id'))

View file

@ -3,6 +3,12 @@ export enum LinkFormat {
WIKILINK = 'wiki-link'
}
export enum BlockIdInsertPosition {
END_OF_BLOCK = 'end-of-block', // 当前块的末尾
NEXT_LINE = 'next-line', // 当前块的下方一行
// NEXT_LINE_WITH_GAP = 'next-line-with-gap' // 当前块的下方两行(中间隔一个空行)
}
export enum ContextType {
NULL = 'null',
HEADING = 'heading',
@ -51,6 +57,7 @@ export interface EasyCopySettings {
calloutCopyPriority: boolean; // Callout 与块ID冲突时优先复制 Callout
autoAddBlockId: boolean; // 是否自动添加 Block ID
allowManualBlockId: boolean; // 是否允许手动输入 Block ID
blockIdInsertPosition: BlockIdInsertPosition; // 块ID的插入位置
autoBlockDisplayText: boolean; // 自动为 Block 添加显示文本
blockDisplayWordLimit: number; // Block 显示文本英文单词限制(按空格分隔)
blockDisplayCharLimit: number; // Block 显示文本字符限制(非英文语言)
@ -81,6 +88,7 @@ export const DEFAULT_SETTINGS: EasyCopySettings = {
calloutCopyPriority: true,
autoAddBlockId: false, // 默认关闭
allowManualBlockId: false, // 默认关闭
blockIdInsertPosition: BlockIdInsertPosition.END_OF_BLOCK, // 默认在块末尾插入
autoBlockDisplayText: true,
blockDisplayWordLimit: 3, // 英文单词限制3个单词
blockDisplayCharLimit: 5, // 字符限制5个字符