見出し画像

テキストエディタで簡易タスクシュート - 運用とコード(Obsidian / 1Writer)

運用が安定してきたので、紹介してみます。

Obsdianや1Writerを使っていて、簡単にタスクシュート的な記録の取り方を試してみたいという人向けです。

テキストエディタでタスクシュートっぽいログを残すために使っているコードのうち、よく使っていて、ある程度動作が安定しているものを載せました。

なお、条件によってうまく動かないこともあるので、あくまで参考まで。

自分はコードを動かす方法がわからずに手間取ったので、[開発メモ]にコードを実行するためのツールの使い方やドキュメントなども書きました。

※ 2024.9.5 運用を変えました。詳しくはこちらの記事に書いています。


できること・できないこと

テキストエディタを使っているのは、軽くて素早くメモが取れるし、コードさえ書ければ自分の好きな仕様にできるからです。

とはいえ、自分でコードを書かない限り機能は増えないので、できることはかなり限られています。開始時刻、終了時刻、タスク名、メモの記録と簡単なルーチン設定ができるだけです。

高機能なTaskChute Cloudなどの各種公式ツールとは違い、終了予定時刻は出ないし、セクションもモードもないです。所要時間や見積時間も基本的に扱わず、終了予定時刻もでません。コードを書けば実現できそうですが、手間がかかりそうで、いまのところ実装していないです。シュミレーションがしたいときは、いまでもTaskChute Cloudを使っています。

それでもテキストエディタを使っているのは、動作が軽く、メモがすごく残しやすいからです。

ここ1ヶ月ほど、仕事環境が変わったこともあり、「実行」のためにルーチン調整などをこまめにやっていく、というよりは、その時々の自分の感情や思考を「記録」することを重視しています。なので、立ち上がりが早くて、サクサクメモが残せるテキストエディタがピッタリなのです。

使っているデバイス・アプリ

ファイルはDropboxに置いてあり、それを1WriterとObsidianから開いて記録をとっていきます。

基本的にはiPhoneの1Writerを使っています。MacやWindowsを使っているときは、Obsidianから操作します。

1Writer

1Writerでは、JavaScriptでアクションと呼ばれるコードを書いて実行することができます。

[開発メモ] アクションの使い方

アクションの作成方法

  1. 左下に表示される三点リーダーをクリックすると「Actions」画面が表示されます。

  2. 画面上部の「+」ボタンを押し、「Add JavaScript Action」を選択します。

  3. Nameに任意のアクション名を設定します。

  4. Edit Scriptをクリックして、コードを書きます。

  5. Iconをクリックして、好きなアイコンを選択します。

アクションの呼び出し方

  1. 左下に表示される三点リーダーをクリックすると「Actions」画面が表示されます。

  2. 作成したアクションを選択すると、アクションが実行されます。

アクションの編集方法

  1. 左下に表示される三点リーダーをクリックすると「Actions」画面が表示されます。

  2. 作成したアクションの右側のℹ️ボタンを選択すると、アクションが編集できます。

[開発メモ] コードを書く・修正するための情報・ツール

ドキュメント: JavaScript Documentation - 1Writer

デバッグに使えるコード

キーボード設定

1Writerのキーボード設定は下記のようにしています。よく使うメモの作成、行削除・移動、開始時刻・終了時刻の打刻を使いやすい場所に配置しています。

左から順に

  1. メモ作成

  2. 行削除

  3. カーソル行を下に移動

  4. カーソル行を上に移動

  5. 開始・終了の打刻(直前タスクの終了時刻)

  6. 開始・終了の打刻(現在時刻)

開始・終了の打刻(現在時刻)

タスクの実行・終了処理をします。開始時刻と終了時刻を打刻して、行頭の記号を変えます。➡️が実行中タスク、✅が完了タスクです。

  • タスクの開始時は、行頭に➡️hh:mm- をつける。開始時刻は現在時刻。

  • タスクの終了時は行頭を更新して、✅hh:mm-hh:mmにする。

  • 完了タスク(行頭が✅のタスク)選択時に実行した場合、該当のタスクを複製して開始時間を現在時刻にする。

なお、タスク名の行頭の記号がうまく変更されず、文字化けした文字がタスク名の前に残ってしまいます。

例)🔲タスク名 ⇒ ➡️�タスク名 ⇒ ✅�タスク名

運用上たいして困らないのでそのまま使っていますが、気になる方はコードを修正して下さい。

動作イメージ

🔲未実行タスク
🔁未実行のルーチン
⏰未実行の予定
➡️14:14- 実行中タスク
✅14:14-14:20 完了タスク

コード

function timestampTool() {
    const currentTime = new Date();
    const formattedTime = currentTime.toTimeString().slice(0, 5);

    // 現在のカーソル行の範囲を取得
    const selectedLineRange = editor.getSelectedLineRange();
    const cursorLineStart = selectedLineRange[0];
    const cursorLineEnd = selectedLineRange[1];
    const currentLineText = editor.getTextInRange(cursorLineStart, cursorLineEnd);

    // 正規表現で各パターンを定義
    const completedTaskRegex = /^✅\d{2}:\d{2}-\d{2}:\d{2}/;
    const arrowTimeRegex = /^➡️(\d{2}:\d{2})-/;
    const startTimeRegex = /^\d{2}:\d{2}-/;
    const taskSymbolRegex = /^[🔲🔁]/; // 🔲 または 🔁 に一致

    let updatedLineText;

    if (completedTaskRegex.test(currentLineText)) {
        // "✅hh:mm-hh:mm" が既にある場合、新しいタスクを次の行に追加
        const lineWithoutTimes = currentLineText.replace(completedTaskRegex, '').trim();
        const newLineText = `➡️${formattedTime}- ${lineWithoutTimes}`;

        // 現在の行をそのままにして、次の行に新しいタスクを挿入
        editor.replaceTextInRange(cursorLineEnd, cursorLineEnd, `\n${newLineText}`);
        const newCursorPosition = cursorLineEnd + newLineText.length + 1; // 改行分も考慮
        editor.setSelectedRange(newCursorPosition);
    } else if (arrowTimeRegex.test(currentLineText)) {
        // "➡️hh:mm-" が既にある場合、"✅"に変更して終了時刻を追加
        updatedLineText = currentLineText.replace(arrowTimeRegex, `✅$1-${formattedTime}`);
        editor.replaceTextInRange(cursorLineStart, cursorLineEnd, updatedLineText);
        editor.setSelectedRange(cursorLineStart + updatedLineText.length);
    } else if (startTimeRegex.test(currentLineText)) {
        // "hh:mm-" が既にある場合、行頭を"✅"に変更して終了時刻を追加
        updatedLineText = currentLineText.replace(startTimeRegex, `✅$&${formattedTime}`);
        editor.replaceTextInRange(cursorLineStart, cursorLineEnd, updatedLineText);
        editor.setSelectedRange(cursorLineStart + updatedLineText.length);
    } else {
        // hh:mm- 形式がない場合、"➡️hh:mm- "を行頭に追加し、🔲または🔁があれば削除
        const lineWithoutTaskSymbol = currentLineText.replace(taskSymbolRegex, '').trim();
        updatedLineText = `➡️${formattedTime}- ${lineWithoutTaskSymbol}`;
        editor.replaceTextInRange(cursorLineStart, cursorLineEnd, updatedLineText);
        editor.setSelectedRange(cursorLineStart + updatedLineText.length);
    }
}

timestampTool();

開始・終了の打刻(直前タスクの終了時刻)

開始・終了の打刻(現在時刻)と基本の動作は同じです。

ちょっとクセがあります。一日の最初のタスクと最後のタスクで実行するとうまく動かないです。

前回タスクの終了時刻を開始時刻にしたくて書いたコードです。また、タスクの実行時に、タスクを完了タスクの直後に自動で移動しました。特にタスク移動は不完全で、挙動が変な時もありますが、とりあえず使えていてまあまあ便利なので紹介します。

function timestampTool() {
    const currentTime = new Date();
    const formattedTime = currentTime.toTimeString().slice(0, 5);

    // 現在のカーソル行の範囲を取得
    const selectedLineRange = editor.getSelectedLineRange();
    const cursorLineStart = selectedLineRange[0];
    const cursorLineEnd = selectedLineRange[1];
    let currentLineText = editor.getTextInRange(cursorLineStart, cursorLineEnd);

    // 正規表現で各パターンを定義
    const completedTaskRegex = /^✅\d{2}:\d{2}-(\d{2}:\d{2})/;
    const startTimeRegex = /^[➡️🔲🔁⏰✅✴]+(\d{2}:\d{2})-/;
    const taskSymbolRegex = /^[🔲🔁⏰✴]/; // 🔲 または 🔁 または ⏰ または ✴に一致


    // ドキュメント全体を取得し、行ごとに分割
    let allText = editor.getText();
    let lines = allText.split('\n');

    let latestEndTime = "00:00"; // 最も遅い終了時刻を初期化

    // 各行をチェックし、最も遅い終了時刻を特定
    lines.forEach(line => {
        const match = line.match(completedTaskRegex);
        if (match) {
            const endTime = match[1];
            if (endTime > latestEndTime) {
                latestEndTime = endTime;
            }
        }
    });

    if (completedTaskRegex.test(currentLineText)) {
        // "✅hh:mm-hh:mm" が既にある場合、新しいタスクを次の行に追加
        const lineWithoutTimes = currentLineText.replace(completedTaskRegex, '').trim();
        updatedTask = `➡️${latestEndTime}- ${lineWithoutTimes}`;
    } else if (startTimeRegex.test(currentLineText)) {
        // "[➡️]+hh:mm-" が既にある場合、"✅"に変更して終了時刻を追加        
        updatedTask = currentLineText.replace(startTimeRegex, (match, startTime) => {
            const duration = calculateDuration(startTime, formattedTime);
            return `✅${startTime}-${formattedTime} (${duration})`;
        });
        editor.replaceTextInRange(cursorLineStart, cursorLineEnd, updatedTask);
        editor.setSelectedRange(cursorLineStart + updatedTask.length);
        return; // ここで処理を終了
    } else {
        // hh:mm- 形式がない場合、"➡️hh:mm- "を行頭に追加し、🔲または🔁または⏰または➡️があれば削除
        const lineWithoutTaskSymbol = currentLineText.replace(taskSymbolRegex, '').trim();
        updatedTask = `➡️${latestEndTime}- ${lineWithoutTaskSymbol}`;
        // 現在の行を削除する(行の終わりの改行も含めて削除)
        const lineEndPosition = cursorLineEnd + 1; // 行の終わり+改行
        editor.replaceTextInRange(cursorLineStart, lineEndPosition, "");
    }

    

    // ドキュメント全体を取得し、行ごとに分割
    allText = editor.getText();
    lines = allText.split('\n');

    let lastCompletedTaskIndex = -1;
    let nextUncompletedTaskIndex = -1;

    // 行を上から順にチェック
    for (let i = 0; i < lines.length; i++) {
        if (completedTaskRegex.test(lines[i])) {
            lastCompletedTaskIndex = i;
        } else if (taskSymbolRegex.test(lines[i]) && lastCompletedTaskIndex !== -1) {
            nextUncompletedTaskIndex = i;
            break;
        }
    }

    // 次の未完了タスクが見つかった場合、その1行手前にタスクを挿入
    if (nextUncompletedTaskIndex !== -1) {
        lines.splice(nextUncompletedTaskIndex, 0, updatedTask);
    } else {
        // 未完了タスクが見つからない場合、最後に追加
        lines.push(updatedTask);
    }

    // 全体テキストを更新
    const updatedText = lines.join('\n');
    editor.setText(updatedText);

    // カーソルを移動したタスク行に設定
    const newCursorPosition = lines.indexOf(updatedTask);
    const targetPosition = lines.slice(0, newCursorPosition).join('\n').length + updatedTask.length + 1;
    editor.setSelectedRange(targetPosition);
}

// 所要時間を計算する関数
function calculateDuration(startTime, endTime) {
    const [startHour, startMinute] = startTime.split(':').map(Number);
    const [endHour, endMinute] = endTime.split(':').map(Number);

    let durationMinutes = (endHour * 60 + endMinute) - (startHour * 60 + startMinute);
    if (durationMinutes < 0) {
        // 終了時刻が開始時刻より前の場合、翌日にまたがっていると仮定
        durationMinutes += 24 * 60;
    }

    const hours = Math.floor(durationMinutes / 60);
    const minutes = durationMinutes % 60;

    return `${hours}:${minutes.toString().padStart(2, '0')}`;
}

timestampTool();

カーソル行の上下移動・削除

テキストを「1行ごとのリスト」ととらえ、削除と移動を可能に〜1Writerカスタマイズ①〜 - iPhoneと本と数学となんやかんやとで紹介されているコードを使わせていただいています。

メモ作成

作成日時をタイトルに新規ノートを作成し、カーソル行にノートへのリンクを追加します。

選択範囲がある場合、選択範囲を切り取ってノートを作成します。一番上の行をタイトルにします。また、作成したノートへのリンクをカーソル位置に入れます。

// 日付フォーマット用の関数
function formatDate(date) {
    const yy = String(date.getFullYear()).slice(-2); // 2桁の年を取得
    const MM = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    const hh = String(date.getHours()).padStart(2, '0');
    const mm = String(date.getMinutes()).padStart(2, '0');
    return `${yy}${MM}${dd} ${hh}${mm}`;
}

// 現在の日時を取得してフォーマット
const now = new Date();
const formattedDate = formatDate(now);

// 選択範囲を取得
const selectedText = editor.getSelectedText();
const selectedRange = editor.getSelectedRange(); // [start, end]
const originalFolderPath = editor.getFolderPath(); // 元のノートのフォルダパスを取得
const originalFileName = editor.getFileName(); // 元のノートのファイル名を取得
const originalTitle = originalFileName.replace(/\.md$/, '');

// タスク名の取得
// 現在の行の範囲を取得
let range = editor.getSelectedLineRange();

// 現在の行のテキストを取得
let lineText = editor.getTextInRange(range[0], range[1]);

// 正規表現で行頭のシンボルや時刻部分を削除
let taskName = lineText.replace(/^[✅🔲🔁⏰➡️]?\s*(\d{2}:\d{2}(?:-\d{2}:\d{2})?)?(\s*\(\d{1,2}:\d{2}\))?\s*�?\s*/g, '').trim();

// テキストをタスク名で置き換え
editor.replaceTextInRange(range.start, range.end, taskName);


let header=`` 
let footer = `\n\nfrom [[${originalTitle}]]`;


if (selectedText.length > 0) {
    // 選択範囲がある場合、既存の処理
    const lines = selectedText.split('\n');
    const title = lines[0].trim();
    const newFilePath = `${originalFolderPath}/${title}.md`;
    const header = `### ${originalTitle}\n\n`;
    const contentToAppend = encodeURIComponent(header + selectedText + footer);
    const appendURL = `onewriter://x-callback-url/append?path=${encodeURIComponent(newFilePath)}&text=${contentToAppend}`;
    app.openURL(appendURL);

    // 元のノートにリンクを挿入
    const originalNotePath = `${originalFolderPath}/${originalFileName}`;
    editor.openFile(originalNotePath, 'edit', function() {
        const link = `[[${title}]]`;
        editor.replaceTextInRange(selectedRange[0], selectedRange[1], link);
        const newCursorPosition = selectedRange[0] + link.length;
        editor.setSelectedRange(newCursorPosition);
    });
} else {
    // 選択範囲がない場合
    const newFilePath = `${originalFolderPath}/${formattedDate}`;

    // カーソル行の末尾にリンクを挿入
    const link = ` [[${formattedDate}]]`;
    const lineRange = editor.getSelectedLineRange();
    editor.replaceTextInRange(lineRange[1], lineRange[1], link);
    
    // カーソル位置をリンクの文末に設定
    const newCursorPosition = lineRange[1] + link.length;
    editor.setSelectedRange(newCursorPosition);

const content = footer;

    // 新しいノートを作成
    editor.newFile(content,`${formattedDate}`, function(){editor.setSelectedRange(header.length)});
}

リピートタスクの一括コピー

リピートタスク.mdから、行頭に🔁または⏰がついたタスクをコピーします。

なお、平日、曜日、月日でリピート頻度の設定ができるようにしています。

指定方法

平日か、曜日か、月日かいずれか一つの条件を設定可能。
曜日、月日は, 区切りで複数設定できます。
平日は祝日を考慮していません。月曜から金曜に生成されます。

🔁{条件を, 区切りで指定、スペースは入れない}{半角スペース}{タスク名}

設定例

🔁平日 平日に実行するタスク(祝日判定は入っていないので、月曜から金曜に生成されます)
🔁日,月 日曜と月曜に実行するタスク
🔁0825,0826 毎年8月25日と8月26日に実行するタスク 

コード

// ノート情報の保存
var folder = editor.getFolderPath(); // 現在のノートのフォルダパスを取得
var editingfile = editor.getFileName();
var cursorPosition = editor.getSelectedRange(); // カーソル位置を保存

// ノートタイトルから日付部分を取得(例: "2023-08-20")
var datePattern = /^(\d{4})-(\d{2})-(\d{2})/;
var match = editingfile.match(datePattern);

if (!match) {
    ui.alert("ノートのタイトルに有効な日付が含まれていません。");
    return;
}

// 日付オブジェクトを作成
var noteDate = new Date(match[1], match[2] - 1, match[3]); // 月は0から始まるため-1

// MMDD形式で現在の日付を取得
var currentMMDD = ("0" + (noteDate.getMonth() + 1)).slice(-2) + ("0" + noteDate.getDate()).slice(-2);

// ノートの日付から曜日を取得(0: 日曜, 1: 月曜, ..., 6: 土曜)
var dayOfWeek = noteDate.getDay();

// 曜日のマッピング(0: 日曜 ~ 6: 土曜)
var dayOfWeekMapping = ['日', '月', '火', '水', '木', '金', '土'];

// リピートタスクを記述しているファイルのファイル名
var openfilename = 'リピートタスク.md';

// リピートタスクのファイルを開く(現在のフォルダ内)
editor.openFile(folder + '/' + openfilename, 'edit', call);

function call() {
    // ファイルのテキストを取得
    var text = editor.getText();

    // 🔁 または ⏰ で始まる行のみをフィルタリング
    let listData = text.split('\n').filter(line => {
        // MMDD形式のチェック(カンマ区切り対応)
        let matchedMMDD = line.match(/^🔁((\d{4},?)+)\s/);
        if (matchedMMDD) {
            let datesArray = matchedMMDD[1].split(',').map(date => date.trim());
            // 現在の日付がリストに含まれているか確認
            return datesArray.includes(currentMMDD);
        }

        // 平日指定の場合
        if (line.startsWith('🔁平日')) {
            return dayOfWeek >= 1 && dayOfWeek <= 5; // 平日なら含める
        }

        // 曜日が指定されているか確認
        let matchedDays = line.match(/🔁([月火水木金土日,]+)\s/);
        if (matchedDays) {
            let daysArray = matchedDays[1].split(',').map(day => day.trim());
            // 現在の日付の曜日が含まれているかを確認
            return daysArray.includes(dayOfWeekMapping[dayOfWeek]);
        }

        return line.startsWith('🔁') || line.startsWith('⏰');
    });

    // 条件部分の削除処理
    listData = listData.map(line => {
        // MMDD部分の削除
        line = line.replace(/^🔁((\d{4},?)+)\s/, '🔁').trim();

        // 平日部分の削除
        line = line.replace('平日 ', '').trim();

        // 曜日部分の削除
        line = line.replace(/🔁([月火水木金土日,]+)\s/, '🔁').trim();

        return line;
    });

    ui.hudDismiss();

    if (listData.length === 0) {
        ui.alert("🔁または⏰で始まるタスクが見つかりませんでした。");
        return;
    }

    const selectedText = listData.join('\n'); // すべてのタスクを1つの文字列に結合

    // 元のノートに戻り、カーソル位置にすべてのタスクを挿入
    editor.openFile(folder + '/' + editingfile, 'edit', function() {
        editor.setSelectedRange(cursorPosition[0]); // カーソル位置に戻る
        editor.replaceSelection(selectedText); // 選択されたテキストを挿入
        editor.setSelectedRange(cursorPosition[0] + selectedText.length); // カーソルを挿入後の位置に移動
    });
}

Obsididan

Templaterというサードパーティープラグインを入れて、自分で書いたコードを実行できるようにします。

[開発メモ] Templaterの使い方

下記は一般的なTemplaterの使い方です。後述のコードを下記の手順で登録することでコードを簡単に呼び出して実行できるようになります。

手順1: Templaterのインストール

  1. Obsidianの設定から、"Community plugins"を選択し、"Community plugins"セクションの"Browse"ボタンを押します。

  2. "Templater"を探して、"Install"を押します。

  3. "Community plugins"で"Templater"を探し、有効化します。

手順2: 必要なフォルダの作成

  1. Templateフォルダを作成する(フォルダ名はなんでもいいです。Templaterで呼び出すテンプレートファイルを入れておくためのフォルダを作ります。)

  2. TemplaterJS フォルダを作成する。(フォルダ名はなんでもいいです。JavaScriptコードをまとめて入れておくためのフォルダを作ります)

手順3: フォルダの紐付け

Templaterのプラグイン設定で、先ほど作成したフォルダを紐づけます。

  1. Obsidianの設定に進み、「Templater」プラグインの設定を開きます。

  2. 「Template folder location」に、「Templates」フォルダを指定します。

  3. 「User Script Folder」に、「TemplaterJS」フォルダのパスを指定します。

  4. 「Template hotkeys」で「Add new hotkeys for template」をクリックし、InsertTimestamp.mdを選択します。

手順4: ファイルの作成

  1. 任意のテンプレートファイルを.md形式で作成して、Templateフォルダに保存

  2. 任意のスクリプトファイルを.js形式で作成して、TemplaterJSフォルダに保存(テンプレートファイルのみで十分な場合は不要)

手順5: Hotkeysの設定

  1. Obsidianの設定ボタンを押し、「Templater」プラグインの設定を開きます。

  2. 「Template hotkeys」のセクションで「Add new hotkeys for template」をクリックし、テンプレートファイルを選択します。Cmd/Ctrl + Pで立ち上げたコマンドペインからコードを呼び出して実行できるようになります。

  3. (任意)Obsidianの設定で「Hotkeys」をクリックし、2で選択したテンプレートファイルの名前を検索して、任意のホットキーを設定します。ショートカットキーでコードを実行できるようになります。

[開発メモ] コードを書く・修正するための情報・ツール

Templaterのドキュメント: Introduction - Templater

開発者ツール(エラーの確認、デバッグに使用)
Mac: Cmd + Opt + I
Windows: Ctrl + Shift + I

開始・終了の打刻(現在時刻)

タスクの実行・終了処理をします。開始時刻と終了時刻を打刻して、行頭の記号を変えます。➡️が実行中タスク、✅が完了タスクです。

  • タスクの開始時は、行頭に➡️hh:mm- をつける。開始時刻は現在時刻。

  • タスクの終了時は行頭を更新して、✅hh:mm-hh:mmにする。

  • 完了タスク(行頭が✅のタスク)選択時に実行した場合、該当のタスクを複製して開始時間を現在時刻にする。

なお、タスク名の行頭の記号がうまく変更されず、文字化けした文字がタスク名の前に残ってしまいます。

例)🔲タスク名 ⇒ ➡️�タスク名 ⇒ ✅�タスク名

運用上たいして困らないのでそのまま使っていますが、気になる方はコードを修正して下さい。

動作イメージ

🔲未実行タスク
🔁未実行のルーチン
⏰未実行の予定
➡️14:14- 実行中タスク
✅14:14-14:20 完了タスク

テンプレートファイル

InsertTimestamp.md

<%* tp.user.timestampTool3(tp) %>

スクリプトファイル

timestampTool3.js

async function timestampTool3(tp) {
    const currentTime = new Date();
    const formattedTime = currentTime.toTimeString().slice(0, 5);

    // 現在のカーソル行を取得
    const editor = app.workspace.activeLeaf.view.editor;
    const cursorLine = editor.getCursor().line;
    const currentLineText = editor.getLine(cursorLine);

    // 正規表現で "➡️hh:mm-"、"✅hh:mm-"、または "✅hh:mm-hh:mm" を探す
    const completedTaskRegex = /^✅\d{2}:\d{2}-\d{2}:\d{2}/;
    const arrowTimeRegex = /^➡️(\d{2}:\d{2})-/;
    const startTimeRegex = /^\d{2}:\d{2}-/;
    const taskSymbolRegex = /^[🔲🔁]/;

    let updatedLineText;
    let cursorPosition;

    if (completedTaskRegex.test(currentLineText)) {
        // "✅hh:mm-hh:mm" が既にある場合、タスクをコピーし次の行に新しいタスクを挿入
        const lineWithoutTimes = currentLineText.replace(completedTaskRegex, '').trim();
        const newLineText = `➡️${formattedTime}- ${lineWithoutTimes}`;
        
        // 現在の行をそのままにして、次の行に新しいタスクを挿入し、その後に空行を追加
        editor.replaceRange(`${newLineText}\n`, { line: cursorLine + 1, ch: 0 });
        cursorPosition = `➡️${formattedTime}- `.length;

        // カーソルを新しい行に移動
        editor.setCursor({ line: cursorLine + 1,  ch:`➡️${formattedTime}- `.length}); // 空行の位置に移動
    } else if (arrowTimeRegex.test(currentLineText)) {
        // "➡️hh:mm-" が既にある場合、"✅"に変更して、終了時刻を追加
        updatedLineText = currentLineText.replace(arrowTimeRegex, `✅$1-${formattedTime}`);
        cursorPosition = updatedLineText.indexOf(formattedTime) + formattedTime.length;
        editor.setLine(cursorLine, updatedLineText);
        editor.setCursor({ line: cursorLine, ch: cursorPosition });
    } else if (startTimeRegex.test(currentLineText)) {
        // "hh:mm-" が既にある場合、行頭を"✅"に変更して、終了時刻を追加
        updatedLineText = currentLineText.replace(startTimeRegex, `✅$&${formattedTime}`);
        cursorPosition = updatedLineText.indexOf(formattedTime) + formattedTime.length;
        editor.setLine(cursorLine, updatedLineText);
        editor.setCursor({ line: cursorLine, ch: cursorPosition });
    } else {
        // hh:mm- 形式がない場合、"➡️hh:mm- "を行頭に追加し、🔲があれば消す
        const lineWithoutTaskSymbol = currentLineText.replace(taskSymbolRegex, '').trim();
        updatedLineText = `➡️${formattedTime}- ${lineWithoutTaskSymbol}`;
        cursorPosition = `➡️${formattedTime}- `.length;
        editor.setLine(cursorLine, updatedLineText);
        editor.setCursor({ line: cursorLine, ch: cursorPosition });
    }
}

module.exports = timestampTool3;

メモ作成

メモを新規作成して、カーソル行の末尾に作成されたメモのリンクをつけます。

  • カーソル行の末尾に[[YYMMDD hhmm]]のリンクをつける。リンクの日時はノートの作成時のもの。

  • タイトルがYYMMDD hhmm、末尾に[[元のノートへのリンク]]が入った新規ノートを作成する。

動作イメージ

タスク一覧側

🔲タスク名 [[240902 1419]]

新規ノート側

ファイル名: 240902 1419


from [[2024-09-02]]

テンプレートファイル

CreateNote.md

<%*

function formatDate(date) {

    const yy = String(date.getFullYear()).slice(-2); // 2桁の年を取得
    const MM = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    const hh = String(date.getHours()).padStart(2, '0');
    const mm = String(date.getMinutes()).padStart(2, '0');

    return `${yy}${MM}${dd} ${hh}${mm}`;

}

// 現在のカーソル行を取得
const editor = app.workspace.activeLeaf.view.editor;
const cursorLine = editor.getCursor().line;
const currentLineText = editor.getLine(cursorLine);

let updatedLineText;
let cursorPosition;

// 現在の日時を取得してフォーマット
const now = new Date();
const formattedDate = formatDate(now);

// 現在のファイルに関する情報を取得
const originalFolderPath = tp.file.folder(true); // 元のノートのフォルダパスを取得
const originalFileName = tp.file.title; // 元のノートのファイル名を取得

try {

		// 新しいファイル名
		const newFilePath = `${formattedDate}`;
    
    // リンクを現在の行の末尾に挿入
    const link = `[[${formattedDate}]]`;
    const updatedLineText = currentLineText.replace(currentLineText, `${currentLineText} ${link}`);
    
    cursorPosition = updatedLineText.indexOf(currentLineText);
    editor.setLine(cursorLine, updatedLineText);
    editor.setCursor({ line: cursorLine, ch: cursorPosition });

    // 新しいノートを作成
    const content = `\nfrom [[${originalFileName}]]`;
    await tp.file.create_new(content, newFilePath, true);

} catch (error) {
    console.error("Error processing without selection:", error);
}

%>

行移動

Hotkeyを割り当てるだけです。

Move line up に Cmd + Shift + ↑
Move line down に Cmd + Shift + ↓

を割り当てています。

読んでいただきありがとうございます。