Reorganize project structure and update configuration files

• Move source files to src/ directory:
  - main.ts → src/main.ts (main plugin class)
  - settings.ts → src/settings.ts (settings interface and UI)
  - sort.ts → src/sort.ts (task sorting and moving functionality)
  - syntax.ts → src/syntax.ts (syntax highlighting with CodeMirror)

• Update build configuration:
  - esbuild.config.mjs: Change entry point from "main.ts" to
"src/main.ts"

• Update plugin manifest:
  - manifest.json: Remove unnecessary "main" field

• Enhance UI styling:
  - styles.css: Add .todo-txt-mode-files-container styling for better
settings layout
This commit is contained in:
Riosk It 2025-06-09 21:19:24 +09:00
parent 42a69ef75f
commit 116449cdc2
7 changed files with 91 additions and 79 deletions

View file

@ -15,7 +15,7 @@ const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
entryPoints: ["src/main.ts"],
bundle: true,
external: [
"obsidian",

View file

@ -6,6 +6,5 @@
"description": "Support for todo.txt file format with syntax highlighting and task management",
"author": "Riosk It",
"authorUrl": "https://github.com/rioskit",
"isDesktopOnly": false,
"main": "main.js"
"isDesktopOnly": false
}

View file

@ -1,9 +1,6 @@
import {
Editor,
MarkdownView,
Plugin,
TFile,
debounce,
} from 'obsidian';
import { TodoTxtSettings, DEFAULT_SETTINGS, TodoTxtSettingTab } from './settings';
@ -20,10 +17,19 @@ export default class TodoTxtPlugin extends Plugin {
this.sorter = new TodoTxtSorter(this.app, this.settings, this.isTodoTxtFile.bind(this));
this.addCommand({
id: 'todo-txt-done',
id: 'done',
name: 'Move completed tasks to done file',
callback: async () => {
await this.sorter.moveCompletedTasks();
checkCallback: (checking: boolean) => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.file) return false;
const isTodoFile = this.isTodoTxtFile(activeView.file.path);
if (checking) return isTodoFile;
if (isTodoFile) {
this.sorter.moveCompletedTasks();
}
return true;
}
});
this.sorter.registerSortCommands(this);
@ -34,36 +40,12 @@ export default class TodoTxtPlugin extends Plugin {
]);
const debouncedEditorChangeHandler = debounce((editor: Editor, markdownView: MarkdownView) => {
const file = markdownView?.file;
if (!file || !this.isTodoTxtFile(file.path)) {
return;
}
}, 100, true);
this.registerEvent(
this.app.workspace.on('editor-change', debouncedEditorChangeHandler)
);
this.registerEvent(
this.app.workspace.on('file-open', (file: TFile | null) => {
if (file && this.isTodoTxtFile(file.path)) {
this.refreshView();
}
})
);
this.app.workspace.onLayoutReady(() => {
this.refreshView();
});
}
onunload() {
if (this.sorter) {
this.sorter = null as unknown as TodoTxtSorter;
}
console.log('Todo.txt Mode plugin unloaded');
}
async loadSettings() {
@ -72,26 +54,9 @@ export default class TodoTxtPlugin extends Plugin {
async saveSettings() {
await this.saveData(this.settings);
this.refreshView();
}
public isTodoTxtFile(path: string): boolean {
return this.settings.todoFilePaths.includes(path) || path === this.settings.doneFilePath;
}
private refreshView() {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.editor) {
return;
}
const editor = activeView.editor;
const content = editor.getValue();
const cursor = editor.getCursor();
if (content && content.length > 0) {
editor.setValue(content);
editor.setCursor(cursor);
}
}
}

View file

@ -1,4 +1,4 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import { App, PluginSettingTab, Setting, normalizePath } from 'obsidian';
import TodoTxtPlugin from './main';
export enum SortType {
@ -109,7 +109,9 @@ export class TodoTxtSettingTab extends PluginSettingTab {
.setPlaceholder(placeholder)
.setValue(this.plugin.settings[key] as string)
.onChange(async (value) => {
(this.plugin.settings[key] as string) = value;
// Normalize path for file path settings
const normalizedValue = (key === 'doneFilePath') ? normalizePath(value) : value;
(this.plugin.settings[key] as string) = normalizedValue;
await this.plugin.saveSettings();
})
);
@ -119,10 +121,9 @@ export class TodoTxtSettingTab extends PluginSettingTab {
const {containerEl}: {containerEl: HTMLElement} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Todo.txt Mode Settings'});
const todoFilesSetting = new Setting(containerEl)
.setName('Todo Files')
.setName('Todo files')
.setDesc('Paths to your todo files (relative to vault root)');
todoFilesSetting.addButton(button => button
@ -143,7 +144,7 @@ export class TodoTxtSettingTab extends PluginSettingTab {
.setPlaceholder('e.g. /path/to/todo.md')
.setValue(path)
.onChange(async (value: string) => {
this.plugin.settings.todoFilePaths[index] = value;
this.plugin.settings.todoFilePaths[index] = normalizePath(value);
await this.plugin.saveSettings();
})
)
@ -161,13 +162,13 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createTextSetting(
containerEl,
'Done File Path',
'Done file path',
'File to store completed tasks (relative to vault root)',
'e.g. /done.md',
'doneFilePath'
);
containerEl.createEl('h3', {text: 'Highlighting'});
new Setting(containerEl).setHeading().setName('Highlighting');
const styleSettingsInfo = containerEl.createEl('div', {
cls: 'todo-txt-style-settings-info',
@ -186,7 +187,7 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createHighlightSetting(
containerEl,
'Highlight Completed Tasks',
'Highlight completed tasks',
'Apply strikethrough to completed tasks (starting with "x ")',
'highlightCompletedTask',
'completedTaskColor'
@ -194,7 +195,7 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createHighlightSetting(
containerEl,
'Highlight Projects',
'Highlight projects',
'Apply color to projects ("+project")',
'highlightProject',
'projectColor'
@ -202,7 +203,7 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createHighlightSetting(
containerEl,
'Highlight Contexts',
'Highlight contexts',
'Apply color to contexts ("@context")',
'highlightContext',
'contextColor'
@ -210,7 +211,7 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createHighlightSetting(
containerEl,
'Highlight Priorities',
'Highlight priorities',
'Apply color to priorities ("(A)", "(1)")',
'highlightPriority',
'priorityColor'
@ -218,17 +219,17 @@ export class TodoTxtSettingTab extends PluginSettingTab {
this.createHighlightSetting(
containerEl,
'Highlight Due Dates',
'Highlight due dates',
'Apply color to due dates ("due:yyyy-mm-dd")',
'highlightDueDate',
'dueDateColor'
);
containerEl.createEl('h3', {text: 'Sort Settings'});
new Setting(containerEl).setHeading().setName('Sort settings');
this.createTextSetting(
containerEl,
'Boundary Marker',
'Boundary marker',
'Lines after this marker will not be sorted. Default: "--"',
'--',
'boundaryMarker'

View file

@ -58,34 +58,70 @@ export class TodoTxtSorter {
registerSortCommands(plugin: Plugin) {
plugin.addCommand({
id: 'todo-txt-sort-priority',
id: 'sort-priority',
name: 'Sort by priority',
callback: async () => {
await this.sortTasks(SortType.Priority);
checkCallback: (checking: boolean) => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.file) return false;
const isTodoFile = this.isTodoTxtFile(activeView.file.path);
if (checking) return isTodoFile;
if (isTodoFile) {
this.sortTasks(SortType.Priority);
}
return true;
}
});
plugin.addCommand({
id: 'todo-txt-sort-project',
id: 'sort-project',
name: 'Sort by project',
callback: async () => {
await this.sortTasks(SortType.Project);
checkCallback: (checking: boolean) => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.file) return false;
const isTodoFile = this.isTodoTxtFile(activeView.file.path);
if (checking) return isTodoFile;
if (isTodoFile) {
this.sortTasks(SortType.Project);
}
return true;
}
});
plugin.addCommand({
id: 'todo-txt-sort-context',
id: 'sort-context',
name: 'Sort by context',
callback: async () => {
await this.sortTasks(SortType.Context);
checkCallback: (checking: boolean) => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.file) return false;
const isTodoFile = this.isTodoTxtFile(activeView.file.path);
if (checking) return isTodoFile;
if (isTodoFile) {
this.sortTasks(SortType.Context);
}
return true;
}
});
plugin.addCommand({
id: 'todo-txt-sort-due-date',
id: 'sort-due-date',
name: 'Sort by due date',
callback: async () => {
await this.sortTasks(SortType.DueDate);
checkCallback: (checking: boolean) => {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (!activeView || !activeView.file) return false;
const isTodoFile = this.isTodoTxtFile(activeView.file.path);
if (checking) return isTodoFile;
if (isTodoFile) {
this.sortTasks(SortType.DueDate);
}
return true;
}
});
}
@ -178,7 +214,9 @@ export class TodoTxtSorter {
const sortedLines = linesAfterBoundary.length > 0
? [...sortedTasks, '', ...linesAfterBoundary]
: sortedTasks;
await this.app.vault.modify(file, sortedLines.join('\n'));
await this.app.vault.process(file, (data) => {
return sortedLines.join('\n');
});
new Notice(`Tasks sorted by ${sortType}`);
}
@ -218,7 +256,9 @@ export class TodoTxtSorter {
if (completedLines.length > 0) {
completedTasks = [...completedTasks, ...completedLines];
await this.app.vault.modify(file, remainingLines.join('\n'));
await this.app.vault.process(file, (data) => {
return remainingLines.join('\n');
});
}
}
@ -241,7 +281,9 @@ export class TodoTxtSorter {
const existingContent = await this.app.vault.cachedRead(doneFile);
const newContent = completedTasks.join('\n') +
(existingContent ? '\n' + existingContent : '');
await this.app.vault.modify(doneFile, newContent);
await this.app.vault.process(doneFile, (data) => {
return newContent;
});
}
new Notice(`Moved ${completedTasks.length} completed task(s) to ${doneFilePath}`);

View file

@ -83,6 +83,11 @@ settings:
display: none;
}
/* Todo files container styling */
.todo-txt-mode-files-container {
margin-bottom: 16px;
}
/* スタイル設定案内メッセージのスタイル */
.todo-txt-style-settings-info {
margin-bottom: 12px;