diff --git a/manifest.json b/manifest.json index 68e96543..acc52dd7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "obsidian-kanban", - "name": "Kanban", - "version": "2.0.51", - "minAppVersion": "1.0.0", - "description": "Create markdown-backed Kanban boards in Obsidian.", - "author": "mgmeyers", - "authorUrl": "https://github.com/mgmeyers/obsidian-kanban", - "helpUrl": "https://publish.obsidian.md/kanban/Obsidian+Kanban+Plugin", - "isDesktopOnly": false + "id": "obsidian-kanban-am", + "name": "Kanban AM", + "version": "2.0.51", + "minAppVersion": "1.0.0", + "description": "Create markdown-backed Kanban boards in Obsidian (AM fork).", + "author": "mgmeyers and lPhiNix", + "authorUrl": "https://github.com/lPhiNix/obsidian-kanban", + "helpUrl": "https://publish.obsidian.md/kanban/Obsidian+Kanban+Plugin", + "isDesktopOnly": false } diff --git a/src/Settings.ts b/src/Settings.ts index 97fce8f9..715cddba 100644 --- a/src/Settings.ts +++ b/src/Settings.ts @@ -72,6 +72,9 @@ export interface KanbanSettings { 'move-dates'?: boolean; 'move-tags'?: boolean; 'move-task-metadata'?: boolean; + 'auto-create-note'?: boolean; + 'auto-create-note-counter'?: number; + 'auto-create-note-name-format'?: string; 'new-card-insertion-method'?: 'prepend' | 'prepend-compact' | 'append'; 'new-line-trigger'?: 'enter' | 'shift-enter'; 'new-note-folder'?: string; @@ -120,6 +123,9 @@ export const settingKeyLookup: Set = new Set([ 'move-dates', 'move-tags', 'move-task-metadata', + 'auto-create-note', + 'auto-create-note-counter', + 'auto-create-note-name-format', 'new-card-insertion-method', 'new-line-trigger', 'new-note-folder', @@ -470,6 +476,93 @@ export class SettingsManager { }) ); + new Setting(contentEl) + .setName(t('Auto-create note on card add')) + .setDesc( + t( + 'When enabled, adding a card will automatically create a linked note (BOARD-ID format) instead of a plain text card.' + ) + ) + .then((setting) => { + let toggleComponent: ToggleComponent; + + setting + .addToggle((toggle) => { + toggleComponent = toggle; + + const [value, globalValue] = this.getSetting('auto-create-note', local); + + if (value !== undefined) { + toggle.setValue(value as boolean); + } else if (globalValue !== undefined) { + toggle.setValue(globalValue as boolean); + } else { + toggle.setValue(false); + } + + toggle.onChange((newValue) => { + this.applySettingsUpdate({ + 'auto-create-note': { + $set: newValue, + }, + }); + }); + }) + .addExtraButton((b) => { + b.setIcon('lucide-rotate-ccw') + .setTooltip(t('Reset to default')) + .onClick(() => { + const [, globalValue] = this.getSetting('auto-create-note', local); + toggleComponent.setValue(!!(globalValue ?? false)); + + this.applySettingsUpdate({ + $unset: ['auto-create-note'], + }); + }); + }); + }); + + new Setting(contentEl) + .setName(t('Auto-create note name format')) + .setDesc( + t( + 'Template for the note name when auto-creating notes. Available tokens: {{board}}, {{board_lower}}, {{board_raw}}, {{id}}, {{millis}}, {{date}}, {{time}}, {{random}}' + ) + ) + .addText((text) => { + const [value, globalValue] = this.getSetting('auto-create-note-name-format', local); + + text.setPlaceholder((globalValue as string) || '{{board}}-{{id}}'); + text.setValue((value as string) || ''); + + text.onChange((newValue) => { + if (newValue) { + this.applySettingsUpdate({ + 'auto-create-note-name-format': { $set: newValue }, + }); + } else { + this.applySettingsUpdate({ + $unset: ['auto-create-note-name-format'], + }); + } + }); + }) + .addExtraButton((b) => { + b.setIcon('lucide-rotate-ccw') + .setTooltip(t('Reset to default')) + .onClick(() => { + const [, globalValue] = this.getSetting('auto-create-note-name-format', local); + const el = b.extraSettingsEl + .closest('.setting-item') + ?.querySelector('input') as HTMLInputElement | null; + if (el) el.value = ''; + + this.applySettingsUpdate({ + $unset: ['auto-create-note-name-format'], + }); + }); + }); + contentEl.createEl('h4', { text: t('Tags') }); new Setting(contentEl) diff --git a/src/components/Item/ItemForm.tsx b/src/components/Item/ItemForm.tsx index 2294603d..a223d522 100644 --- a/src/components/Item/ItemForm.tsx +++ b/src/components/Item/ItemForm.tsx @@ -14,9 +14,16 @@ interface ItemFormProps { editState: EditState; setEditState: Dispatch>; hideButton?: boolean; + onAutoCreate?: () => void; } -export function ItemForm({ addItems, editState, setEditState, hideButton }: ItemFormProps) { +export function ItemForm({ + addItems, + editState, + setEditState, + hideButton, + onAutoCreate, +}: ItemFormProps) { const { stateManager } = useContext(KanbanContext); const editorRef = useRef(); @@ -70,7 +77,13 @@ export function ItemForm({ addItems, editState, setEditState, hideButton }: Item
diff --git a/src/components/Lane/LaneHeader.tsx b/src/components/Lane/LaneHeader.tsx index 2660b899..4795a12d 100644 --- a/src/components/Lane/LaneHeader.tsx +++ b/src/components/Lane/LaneHeader.tsx @@ -21,6 +21,7 @@ interface LaneHeaderProps { laneIndex: number; bindHandle: (el: HTMLElement) => void; setIsItemInputVisible?: Dispatch>; + onAutoCreate?: () => void; isCollapsed: boolean; toggleIsCollapsed: () => void; } @@ -30,6 +31,7 @@ interface LaneButtonProps { editState: EditState; setEditState: Dispatch>; setIsItemInputVisible?: Dispatch>; + onAutoCreate?: () => void; } function LaneButtons({ @@ -37,6 +39,7 @@ function LaneButtons({ editState, setEditState, setIsItemInputVisible, + onAutoCreate, }: LaneButtonProps) { const { stateManager } = useContext(KanbanContext); return ( @@ -55,7 +58,13 @@ function LaneButtons({ setIsItemInputVisible({ x: 0, y: 0 })} + onClick={() => { + if (onAutoCreate) { + onAutoCreate(); + } else { + setIsItemInputVisible({ x: 0, y: 0 }); + } + }} onDragOver={(e) => { if (getDropAction(stateManager, e.dataTransfer)) { setIsItemInputVisible({ x: 0, y: 0 }); @@ -85,6 +94,7 @@ export const LaneHeader = memo(function LaneHeader({ laneIndex, bindHandle, setIsItemInputVisible, + onAutoCreate, isCollapsed, toggleIsCollapsed, }: LaneHeaderProps) { @@ -161,6 +171,7 @@ export const LaneHeader = memo(function LaneHeader({ editState={editState} setEditState={setEditState} setIsItemInputVisible={setIsItemInputVisible} + onAutoCreate={onAutoCreate} settingsMenu={settingsMenu} /> diff --git a/src/components/helpers.ts b/src/components/helpers.ts index fb354b2d..d411037e 100644 --- a/src/components/helpers.ts +++ b/src/components/helpers.ts @@ -33,6 +33,46 @@ export function generateInstanceId(len: number = 9): string { .slice(2, 2 + len); } +export const DEFAULT_NOTE_NAME_FORMAT = '{{board}}-{{id}}'; + +/** + * Resolves a note name format string, replacing tokens with actual values. + * + * Available tokens: + * {{board}} — board name, uppercased, spaces → "-" + * {{board_lower}} — board name, lowercased, spaces → "-" + * {{board_raw}} — board name as-is, spaces → "-" + * {{id}} — autoincremental counter (persisted in board settings) + * {{millis}} — current timestamp in milliseconds + * {{date}} — current date using the board's date-format setting + * {{time}} — current time using the board's time-format setting + * {{random}} — short random alphanumeric string (6 chars) + */ +export function resolveNoteNameFormat( + format: string, + stateManager: StateManager, + nextCounter: number +): string { + const basename = stateManager.file.basename; + const boardUpper = basename.toUpperCase().replace(/\s+/g, '-'); + const boardLower = basename.toLowerCase().replace(/\s+/g, '-'); + const boardRaw = basename.replace(/\s+/g, '-'); + + const dateFormat = (stateManager.getSetting('date-format') as string) || 'YYYY-MM-DD'; + const timeFormat = (stateManager.getSetting('time-format') as string) || 'HH:mm'; + const now = moment(); + + return format + .replace(/\{\{board\}\}/g, boardUpper) + .replace(/\{\{board_lower\}\}/g, boardLower) + .replace(/\{\{board_raw\}\}/g, boardRaw) + .replace(/\{\{id\}\}/g, String(nextCounter)) + .replace(/\{\{millis\}\}/g, String(Date.now())) + .replace(/\{\{date\}\}/g, now.format(dateFormat)) + .replace(/\{\{time\}\}/g, now.format(timeFormat)) + .replace(/\{\{random\}\}/g, generateInstanceId(6)); +} + export function maybeCompleteForMove( sourceStateManager: StateManager, sourceBoard: Board, diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 70f6710e..e6bb58a5 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -215,6 +215,12 @@ const en = { 'Card title...': 'Card title...', 'Add card': 'Add card', 'Add a card': 'Add a card', + 'Auto-create note on card add': 'Auto-create note on card add', + 'When enabled, adding a card will automatically create a linked note (BOARD-ID format) instead of a plain text card.': + 'When enabled, adding a card will automatically create a linked note (BOARD-ID format) instead of a plain text card.', + 'Auto-create note name format': 'Auto-create note name format', + 'Template for the note name when auto-creating notes. Available tokens: {{board}}, {{board_lower}}, {{board_raw}}, {{id}}, {{millis}}, {{date}}, {{time}}, {{random}}': + 'Template for the note name when auto-creating notes. Available tokens: {{board}}, {{board_lower}}, {{board_raw}}, {{id}}, {{millis}}, {{date}}, {{time}}, {{random}}', // components/Item/ItemMenu.ts 'Edit card': 'Edit card',