Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/autoCompleteParent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,16 @@ function findTaskCompletion(tr: Transaction): {
) => {
// If a change involves inserting an 'x' character
const insertedText = inserted.toString();
if (insertedText === "x" || insertedText === "X") {
console.log(insertedText);
if (
insertedText === "x" ||
insertedText === "X" ||
insertedText.trim() === "- [x]"
) {
// Get the position context
const pos = fromB;
const line = tr.newDoc.lineAt(pos);
const lineText = line.text;
console.log(lineText);

// Check if this is a task being completed ([ ] to [x])
// Matches the pattern where the cursor is between [ and ]
Expand Down
14 changes: 6 additions & 8 deletions src/taskProgressBarIndex.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import {
App,
debounce,
Editor,
editorEditorField,
editorInfoField,
HoverParent,
HoverPopover,
MarkdownRenderer,
Plugin,
PluginSettingTab,
Setting,
} from "obsidian";
import { HTMLElementWithView, taskProgressBarExtension } from "./widget";
import { taskProgressBarExtension } from "./widget";
import { updateProgressBarInElement } from "./readModeWidget";
import {
DEFAULT_SETTINGS,
Expand All @@ -20,6 +14,7 @@ import {
} from "./taskProgressBarSetting";
import { EditorView } from "@codemirror/view";
import { autoCompleteParentExtension } from "./autoCompleteParent";
import { taskStatusSwitcherExtension } from "./taskStatusSwitcher";

class TaskProgressBarPopover extends HoverPopover {
plugin: TaskProgressBarPlugin;
Expand Down Expand Up @@ -106,8 +101,11 @@ export default class TaskProgressBarPlugin extends Plugin {
this.addSettingTab(new TaskProgressBarSettingTab(this.app, this));
this.registerEditorExtension([
taskProgressBarExtension(this.app, this),

]);
this.settings.enableTaskStatusSwitcher &&
this.registerEditorExtension([
taskStatusSwitcherExtension(this.app, this),
]);
this.registerMarkdownPostProcessor((el, ctx) => {
updateProgressBarInElement({
plugin: this,
Expand Down
177 changes: 177 additions & 0 deletions src/taskProgressBarSetting.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { App, PluginSettingTab, Setting, Modal } from "obsidian";
import TaskProgressBarPlugin from "./taskProgressBarIndex";
import { allStatusCollections } from "./task-status";
import { STATE_MARK_MAP, TaskState } from "./taskStatusSwitcher";

export interface TaskProgressBarSettings {
showProgressBar: boolean;
addTaskProgressBarToHeading: boolean;
enableHeadingProgressBar: boolean;
addNumberToProgressBar: boolean;
Expand Down Expand Up @@ -37,9 +39,15 @@ export interface TaskProgressBarSettings {
max: number;
text: string;
}>;

// Task status switcher settings
enableTaskStatusSwitcher: boolean;
taskStatusCycle: string[];
taskStatusMarks: Record<string, string>;
}

export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
showProgressBar: false,
addTaskProgressBarToHeading: false,
enableHeadingProgressBar: false,
addNumberToProgressBar: false,
Expand Down Expand Up @@ -76,6 +84,16 @@ export const DEFAULT_SETTINGS: TaskProgressBarSettings = {
{ min: 60, max: 80, text: "Good progress {{PROGRESS}}%" },
{ min: 80, max: 100, text: "Almost there {{PROGRESS}}%" },
],

// Task status switcher settings
enableTaskStatusSwitcher: false,
taskStatusCycle: ["TODO", "DOING", "IN-PROGRESS", "DONE"],
taskStatusMarks: {
TODO: " ",
DOING: "-",
"IN-PROGRESS": ">",
DONE: "x",
},
};

export class TaskProgressBarSettingTab extends PluginSettingTab {
Expand All @@ -102,6 +120,18 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {

containerEl.createEl("h2", { text: "📍 Task Progress Bar" });

new Setting(containerEl)
.setName("Show progress bar")
.setDesc("Toggle this to show the progress bar.")
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.showProgressBar)
.onChange(async (value) => {
this.plugin.settings.showProgressBar = value;
this.applySettingsUpdate();
})
);

new Setting(containerEl)
.setName("Add progress bar to Heading")
.setDesc(
Expand Down Expand Up @@ -501,6 +531,153 @@ export class TaskProgressBarSettingTab extends PluginSettingTab {
);
}

this.containerEl.createEl("h2", { text: "Task Status Switcher" });

new Setting(containerEl)
.setName("Enable Task Status Switcher")
.setDesc(
"Enable/disable the ability to cycle through task states by clicking."
)
.addToggle((toggle) => {
toggle
.setValue(this.plugin.settings.enableTaskStatusSwitcher)
.onChange(async (value) => {
this.plugin.settings.enableTaskStatusSwitcher = value;
this.applySettingsUpdate();
});
});

new Setting(containerEl)
.setName("Task Status Cycle and Marks")
.setDesc(
"Define task states and their corresponding marks. The order from top to bottom defines the cycling sequence."
);

// Create a container for the task states list
const taskStatesContainer = containerEl.createDiv({
cls: "task-states-container",
});

// Function to refresh the task states list
const refreshTaskStatesList = () => {
// Clear the container
taskStatesContainer.empty();

// Get current cycle and marks
const cycle = this.plugin.settings.taskStatusCycle;
const marks = this.plugin.settings.taskStatusMarks;

// Add each status in the cycle
cycle.forEach((state, index) => {
const stateRow = taskStatesContainer.createDiv({
cls: "task-state-row",
});

// Create the setting
const stateSetting = new Setting(stateRow)
.setName(`Status #${index + 1}`)
.addText((text) => {
text.setValue(state)
.setPlaceholder("Status name")
.onChange((value) => {
// Update the state name in both cycle and marks
const oldState = cycle[index];
cycle[index] = value;

// If the old state had a mark, preserve it with the new name
if (oldState in marks) {
marks[value] = marks[oldState];
delete marks[oldState];
}

this.applySettingsUpdate();
});
})
.addText((text) => {
text.setValue(marks[state] || " ")
.setPlaceholder("Mark")
.onChange((value) => {
// Only use the first character
const mark = value.trim().charAt(0) || " ";
marks[state] = mark;
this.applySettingsUpdate();
});
text.inputEl.maxLength = 1;
text.inputEl.style.width = "40px";
});

// Add buttons for moving up/down and removing
stateSetting.addExtraButton((button) => {
button
.setIcon("arrow-up")
.setTooltip("Move up")
.onClick(() => {
if (index > 0) {
// Swap with the previous item
[cycle[index - 1], cycle[index]] = [
cycle[index],
cycle[index - 1],
];
this.applySettingsUpdate();
refreshTaskStatesList();
}
});
button.extraSettingsEl.style.marginRight = "0";
});

stateSetting.addExtraButton((button) => {
button
.setIcon("arrow-down")
.setTooltip("Move down")
.onClick(() => {
if (index < cycle.length - 1) {
// Swap with the next item
[cycle[index], cycle[index + 1]] = [
cycle[index + 1],
cycle[index],
];
this.applySettingsUpdate();
refreshTaskStatesList();
}
});
button.extraSettingsEl.style.marginRight = "0";
});

stateSetting.addExtraButton((button) => {
button
.setIcon("trash")
.setTooltip("Remove")
.onClick(() => {
// Remove from cycle
cycle.splice(index, 1);
// Don't remove from marks to preserve settings
this.applySettingsUpdate();
refreshTaskStatesList();
});
button.extraSettingsEl.style.marginRight = "0";
});
});

// Add button to add new status
const addButtonContainer = taskStatesContainer.createDiv();
new Setting(addButtonContainer).addButton((button) => {
button
.setButtonText("Add Status")
.setCta()
.onClick(() => {
// Add a new status to the cycle with a default mark
const newStatus = `STATUS_${cycle.length + 1}`;
cycle.push(newStatus);
marks[newStatus] = " ";
this.applySettingsUpdate();
refreshTaskStatesList();
});
});
};

// Initial render of the task states list
refreshTaskStatesList();

this.containerEl.createEl("h2", { text: "Say Thank You" });

new Setting(containerEl)
Expand Down
Loading