import { getEachLatestDate } from "../getEachLatestDate/mod.ts"; import type { NewPageHook, Updater } from "../custom-new-page/mod.ts"; import { parse, toString, isTask } from "../takker99%2Ftakker-scheduler/deps.ts"; import type { Interval } from "../takker99%2Ftakker-scheduler/deps.ts"; import { getIndentCount, toTitleLc } from "../scrapbox-userscript-std/text.ts"; import { lightFormat } from "../date-fns/mod.ts"; import { parse as parseTaskLink } from "../takker99%2Ftakker-scheduler/workflow.ts"; const foodType = { "あさごはん": "#log-breakfast", "ひるごはん": "#log-lunch", "よるごはん": "#log-dinner", } as Record; /** task lineの配下に書いたものを新しいページに切り出す * * 食べたもの記録のときは若干Templateを変える */ export const taskLineHook: NewPageHook = ( text, { title, projectTo, mode }, ) => { const [taskLine, ...lines] = text.split("\n"); const parsed = parse(taskLine); // task lineでない場合は対象外 if (!parsed) return; // 箇条書きが無い時は何も切り出さない if (lines.length === 0) return { text, pages: [] }; const { title: taskName, base, record, ...rest } = parsed; const newTitle = makeTaskTitle(taskName, base, record); const newTaskLine = toString({ title: `[${newTitle}]`, base, record, ...rest, }); // 余計なインデントを削る const minIndentNum = Math.min(...lines.map((line) => getIndentCount(line))); const newLines = [ // タスク名にリンクが入っていたときはそれを使う // ただし、ページタイトルと同名のときは挿入しない ...(/[\[\]]/.test(taskName) && taskName !== `[${newTitle}]` ? [taskName] : []), ...((title in foodType) ? [ ...lines.map( (line) => line.slice(minIndentNum) ), "", foodType[title], '#log-eatenfood' ] : [ ...lines.map( (line) => ` ${line.slice(minIndentNum)}` ), "", ] ), ]; return { text: newTaskLine, pages: [{ project: projectTo, title: newTitle, lines: makeUpdater(title, newLines, [record.start ?? base]), mode, }], }; }; taskLineHook.hookName = "task-hook"; const makeTaskTitle = (title: string, base: Date, record: Interval) => { const date = lightFormat(record.start ?? base, 'yyyy-MM-dd'); if (title in foodType) { return `${date} ${title}`; } // タスクリンクの場合は、それをそのまま使う if (parseTaskLink(title.slice(1, -1))) { return title.slice(1, -1); } const first = title.replace(/[\[\]]/g, '').trim(); return `${first}${first.endsWith(date) ? "" : ` ${date}`}`; }; export const newPageHook: NewPageHook = ( text, { title: titleFrom, projectTo, mode, lines: metaLines }, ) => { const [rawTitle, ...lines] = text.split("\n"); // 複数行の切り出しのみ対象 if (lines.length === 0) return; const title = rawTitle.replace(/[\[\]]/g, "").trim(); // 余計なインデントを削る const minIndentNum = Math.min(...lines.map((line) => getIndentCount(line))); // 行の更新日時と現在日時から日付タグを作る // "takker"で始まるproject以外ではoffにする const dates = projectTo.startsWith("takker") ? getEachLatestDate([ new Date(), ...metaLines.map(({ updated }) => new Date(updated * 1000)), ]) .sort((a, b) => b.getTime() - a.getTime()) : []; const newLines = [ "", // タイトルにリンクが入っていたときはそれを使う ...(/[\[\]]/.test(rawTitle) ? [rawTitle.trimStart()] : []), ...lines.map((line) => line.slice(minIndentNum)), ]; return { text: `${" ".repeat(getIndentCount(rawTitle))}[${title}]`, pages: [{ project: projectTo, title, lines: makeUpdater(titleFrom, newLines, dates), mode, }], }; }; newPageHook.hookName = "new-page-hook"; export const splittedLinkHook: NewPageHook = ( text, { projectTo, mode }, ) => { // リンクを含む単一行のみ対象 if (text.includes("\n") || !/\[[^\]]+\]/.test(text)) return; const [indent, str] = text.match(/^(\s*)(.*)$/)?.slice?.(1) ?? ["", text]; const linksLcInText = [...str.matchAll(/\[([^\]])\]/g)] .map((match) => toTitleLc(match[1])); const title = str.replaceAll("[", "").replaceAll("]", ""); return { text: `${indent}[${title}]`, pages: [{ project: projectTo, title, lines: (prev, { links }) =>{ // すでに同じリンクが書き込まれているときは何もしない const linksLc = links.map((link) => toTitleLc(link)); if (linksLcInText.every( (linkLc) => linksLc.includes(linkLc) )) return; return [ ...prev.map((line) => line.text), str, ]; }, mode, }], }; }; splittedLinkHook.hookName = "splitted-link-hook"; const makeUpdater = (titleFrom: string, lines: Iterable, dates: Date[]): Updater => (prev, { links }) => { const linksLc = links.map((link) => toTitleLc(link)); const fromLine = linksLc.includes(toTitleLc(titleFrom)) ? [] : [`from [${titleFrom}]`]; const dateTags = dates.flatMap( (date) => { const yyyyMMdd = lightFormat(date, "yyyy-MM-dd"); if (linksLc.includes(yyyyMMdd)) return []; const HHmmss = lightFormat(date, "HH:mm:ss"); return [`#${yyyyMMdd} ${HHmmss}`]; } ); const prevLines = prev.map((line) => line.text); /** the index where the first dateTag starts */ const index = prevLines.findIndex( (line) => /^#\d{4}-\d{2}-\d{2}/.test(line) ); const insertBefore = index < 0 ? prevLines.length : index; return [ ...prevLines.slice(0, insertBefore), ...fromLine, ...lines, "", ...dateTags, ...prevLines.slice(insertBefore), ]; };