diff --git a/README.md b/README.md index b04ff51..dfa791e 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,37 @@ This plugin creates a custom view that displays the outlines of multiple daily n ![demo](others/demo.gif) -## New experimental function for v1.4.0 - Show backlink files -Daily Note Outline v1.4.0 adds displaying backlink files.(Settings -> Basics -> Show backlink files)
-In my use case, I have created a template file like `[[{{date}}]]` and assigned a hotkey with the Hotkeys for templates plugin by @Vinzent03 (https://github.com/Vinzent03/obsidian-hotkeys-for-templates) to insert a link to the daily note for that day.
-Users who create daily notes every day will find this convenient, as they can see the note with the date inserted from DNO view. +## New Feature: Create Daily Notes for Unresolved Links -**Notes** -From a speed perspective, I strongly recommend installing Dataview plugin when turning this feature on.
-The Dataview plugin caches the vault's backlink information, and DNO attempts to use its cache when Dataview plugin is activated. In this case, the backlink information can be retrieved significantly faster than without Dataview. +In Daily Note Outline v1.5.0, a feature to create daily notes for unresolved links has been implemented. -### Usage -- After activating the feature, the current granularity (day/week/month/quarter/year) and calendar set name will be displayed on DNO's view. -- Left-clicking on them will toggle the granularity or calendar set to display in sequence. Right-clicking on them will show a list and allow you to select one. +In short, here's how it works: -![SS2](others/pn.png) +If you're using the daily note format YYYY-MM-DD.md, and there's a link like `[[2024-01-01]]` in any note in your vault, but 2024-01-01.md doesn't exist, it will create a new 2024-01-01.md. + +- This feature is for detecting backlinks to daily notes. + +- For example, if you create a template `[[{date}]]` and assign a hotkey to it using the Hotkeys for Templates plugin by @Vinzent03, you can insert a link to the daily note for that day inside your notes. If you have the showBacklinks setting enabled in DNO, files that link to that date will be listed as an outline. If the corresponding daily note doesn't exist, that backlink won't show up in the outline. However, with this new feature, DNO will detect unresolved links to daily notes and create the corresponding daily notes, allowing all daily note links to be displayed in the outline. + +- Therefore, this feature is unnecessary for those who create daily notes every day. + +- Also, this feature is unnecessary for those who don't display backlinks in DNO. + +- There are two ways to use this feature: + + - Manual: Run the "Create daily notes for unresolved links" command from the command palette to detect unresolved links and create the corresponding daily notes. + + - Automatic: Turn on "Create daily notes corresponding to unresolved links at startup" in Settings -> Others, and the same command will run automatically when the view starts up. + +**Note 1** +If you have a large number of links to non-existent daily notes, many daily notes may be created. (A notification to show progress will be shown if more than 5 DNs are created at once).
+Therefore, the first run may take a long time. Subsequent runs will be much quicker. + +**Note 2** +If you're using the Daily Notes core plugin or the regular Periodic Notes plugin, links to past dates will create empty daily notes, while links to the current day or future dates will create daily notes with the template applied.
+However, if you're using the beta version of Periodic Notes (1.0.0-beta3), the template will also be applied when creating daily notes for past dates.
+This is because the beta version of Periodic Notes automatically applies the template when it detects that a daily note with zero content has been created. ## Getting started @@ -51,11 +67,28 @@ To display periodic notes in DNO, the following steps are required. - Install and activate Periodic Notes community plugin and properly configure the granularity and folder paths to be used. - Activate "periodic notes" and "calendar sets" in the "Periodic Notes" section of the DNO settings. +#### Usage +- After activating the feature, the current granularity (day/week/month/quarter/year) and calendar set name will be displayed on DNO's view. +- Left-clicking on them will toggle the granularity or calendar set to display in sequence. Right-clicking on them will show a list and allow you to select one. + +![SS2](others/pn.png) + + **Notes** - Calendar sets is a feature added in Periodic Notes v1.0.0-beta version and is not available in v0.0.17. To use the calendar set feature, you must install the beta version of Periodic Notes, e.g., by using Obsidian BRAT plugin. - Also, since calendar sets is a beta feature of Periodic Notes, there is a possibility that it may not be available in DNO in the future due to specification changes in Periodic Notes. - If you find any problems, please let me know at the forum or GitHub repository. +## Display backlink files + +Daily Note Outline v1.4.0 adds displaying backlink files.(Settings -> Basics -> Show backlink files)
+In my use case, I have created a template file like `[[{{date}}]]` and assigned a hotkey with the Hotkeys for templates plugin by @Vinzent03 (https://github.com/Vinzent03/obsidian-hotkeys-for-templates) to insert a link to the daily note for that day.
+Users who create daily notes every day will find this convenient, as they can see the note with the date inserted from DNO view. + +**Notes** +From a speed perspective, I strongly recommend installing Dataview plugin when turning this feature on.
+The Dataview plugin caches the vault's backlink information, and DNO attempts to use its cache when Dataview plugin is activated. In this case, the backlink information can be retrieved significantly faster than without Dataview. + ### Simple filter / Include / Exclude In order to hide unnecessary items and display only the necessary ones, three types of filter functions are implemented: **Simple filter**, **Include**, and **Exclude**. @@ -222,6 +255,28 @@ If you like my plugin, I would appreciate it if you could buy me a cup of coffee - better preview ## Changelog +- 1.5.0 + - New Features + - Support for List Callouts plugin and Time list + - List items marked by the List Callouts plugin by @mgmeyers are displayed with coloring. + - List items with timestamps (`- HH:mm text`), used in Thino by @Boninall and some scripts, can now be assigned a distinct icon as Time List. + - List callouts and Time lists can be extracted from the context menu of the extract icon. + - Date Jumping + - The behavior of the date range displayed at the top of DNO view has been revamped. + - Clicking on the date part will display a modal for entering a date, and you can jump to the entered date. + - Right-clicking on the date part allows you to select a date candidate to jump to. + - Added a feature to create daily notes if unresolved links to non-existent daily notes are present. (See description in README for details). + - Outline element text can now be wrapped (Settings -> Appearance -> Wrap outline element text). + - Changes + - "Forward search" has been deprecated. If you want to display past dates on startup, enable Settings -> Set base date as home. + - Improvements + - Embedded links are now treated as link outline elements. + - You can now change the icon for backlinks. + - Tooltip preview is now displayed even for daily notes without outline elements. + - Separated lists and tasks into different items in the settings screen for better intuition. + - Added a delay time setting for startup(Settings -> Others -> Startup delay time). If daily notes fail to load on startup, increasing the delay time may resolve the issue. +- Fixed + - Fixed an issue in Reading view where clicking on an outline element would not jump to that position properly. Now the position of the outline element is highlighted when you jump to it. - 1.4.1 - Improvement - Added `show links in properties` in the settings @@ -356,19 +411,24 @@ If you like my plugin, I would appreciate it if you could buy me a cup of coffee ![demo](others/demo.gif) -## v1.4.0の試験的機能 - バックリンクファイルの表示 -Daily Note Outline v1.4.0では、バックリンクファイルの表示を追加しています。設定のBasics -> Show backlink filesを有効化してください。
-私の場合、`[[{{date}}]]`というテンプレートファイルを作成し、Hotkeys for templatesプラグインby @Vinzent03(https://github.com/Vinzent03/obsidian-hotkeys-for-templates)でホットキーを割り当て、ノートに当日デイリーノートへのリンクを挿入できるようにしています。
-毎日デイリーノートを作成している方には、日付が挿入したノートをDNOから参照できて便利かと思います。
+## 新機能: 未解決リンクに対するデイリーノートの作成 Create daily notes for unresolved links -**注意点** -- 動作速度の観点から、バックリンクの表示をオンにする場合、Dataviewプラグインをインストールすることを強くお勧めします。Dataviewプラグインはバックリンクファイルの情報をキャッシュしており、有効化されているとDNOはそのキャッシュ情報を利用するため、処理がずっと速くなります。
+Daily Note Outline v1.5.0で、未解決リンクに対応するデイリーノートを作成する機能が実装されました。 +端的に言うとこのような動作をします: +もしあなたがYYYY-MM-DD.mdという形式のデイリーノートフォーマットを使用していたとして、 +例えば`[[2024-01-01]]`というリンクがvault内のいずれかのノートにあるのに、 +2024-01-01.mdが存在しない場合、新たに2024-01-01.mdを作成します。 -### 使用法 -- 機能を有効化すると、DNOのview上方に現在表示している粒度(day/week/month/quarter/year)やカレンダーセット名が表示されるようになります。 -- それらを左クリックすると表示される粒度やカレンダーセットが順次切り替わります。右クリックすると一覧が表示され、選択できます。 +- この機能はデイリーノートへのバックリンクを検出するための物です。 +- 例えば`[[{date}]]` というテンプレートを作成し、Hotkeys for templatesプラグインby @Vinzent03でホットキーを割り当てると、ノート内にその日のデイリーノートへのリンクを張ることができます。このようなリンクが存在する場合、DNOでshowBacklinksの設定をオンにしていると、その日付にリンクしたファイルがアウトラインに一覧として表示されます。対応するデイリーノートが未作成だとそのバックリンクはアウトラインに表示されませんが、本機能でデイリーノートへの未解決リンクを検出し、対応するデイリーノートを作成することで、全てのデイリーノートへのリンクがアウトラインとして表示可能になります。 +- そのため、毎日デイリーノートを作成している人はこの機能は全く不要です。 +- また、DNOにおいてバックリンクの表示を行わない人にもこの機能は全く不要です。 +- この機能を利用するには2つの方法があります。 + - 手動:コマンドパレットから、Create daily notes for unresolved linksコマンドを実行すると、未解決リンクを検出して対応するデイリーノートを作ります。 + - 自動:設定->othersからCreate daily notes corresponding to unresolved links at startupをオンにすると、viewを起動するときに自動的に同じコマンドを実行します。 +- 注意1:実体のないデイリーノートへのリンクを多数作成している場合、多数のデイリーノートが作成される可能性があります。(1度に5個以上のDNを作成する場合は進捗が表示されます)。そのため、初回実行時のみ長い時間がかかる可能性があります。2回目以降にかかる時間は僅かです。 +- 注意2:デイリーノートコアプラグイン、または通常のPeriodic Notesプラグインを使用している場合、過去の日付に対するリンクについては空のデイリーノートを、当日または未来のデイリーノートへのリンクの場合はテンプレートを適用したデイリーノートを作成します。一方、Periodic Notesのベータ版(1.0.0-beta3)を使用している場合、過去の日付のデイリーノートを作成した場合もテンプレートが適用されます。これは、ベータ版のPeriodic Notesでは、容量0のデイリーノートが作成されたのを検出した場合、テンプレートを自動的に適用する処理が為されているからです。 -![SS2](others/pn.png) ## Getting started はじめ方 本プラグインをcommunityプラグインリストからインストールし、有効化して下さい。
@@ -386,16 +446,32 @@ Daily NoteコアプラグインもしくはPeriodic Notesプラグインが有 使用にあたり、まず設定画面で各アウトライン要素(見出し、リンク、タグ、リスト項目)ごとに表示/非表示を設定することをお勧めします。 ## Feature 機能 -## Periodic Notes pluginのサポート +### Periodic Notes pluginのサポート Daily Note Outline v1.0.0以降では、periodic notesへ対応しています。即ち、weekly/monthly/quarterly/yearly note、およびカレンダーセットへの対応です。
(periodic notesについては詳しくは https://github.com/liamcain/obsidian-periodic-notes を参照してください)
periodic notesをDNOで表示するには、以下のステップが必要です。
- Periodic Notes communityプラグインをインストール、有効化し、使用する粒度やフォルダパスなどの設定を適切に行う。
- DNOの設定の「Periodic Notes」セクションにおいて、「periodic notes」や「calendar sets」を有効化する。
+#### 使用法 +- 機能を有効化すると、DNOのview上方に現在表示している粒度(day/week/month/quarter/year)やカレンダーセット名が表示されるようになります。 +- それらを左クリックすると表示される粒度やカレンダーセットが順次切り替わります。右クリックすると一覧が表示され、選択できます。 + +![SS2](others/pn.png) + + **注意点** - カレンダーセットはperiodic notes v1.0.0-beta版で追加された機能であり、v0.0.17でなく、obsidian BRATプラグインを利用するなどして、ベータ版のPeriodic Notesをインストールしないと利用できません。また、ベータ版の機能であることから、今後の仕様変更などに際して利用できなくなる可能性があります。
- 不具合など合った場合は、forumやGitHubリポジトリまで是非お知らせ下さい。
+ +### バックリンクファイルの表示 +Daily Note Outline v1.4.0では、バックリンクファイルの表示を追加しています。設定のBasics -> Show backlink filesを有効化してください。
+私の場合、`[[{{date}}]]`というテンプレートファイルを作成し、Hotkeys for templatesプラグインby @Vinzent03(https://github.com/Vinzent03/obsidian-hotkeys-for-templates)でホットキーを割り当て、ノートに当日デイリーノートへのリンクを挿入できるようにしています。
+毎日デイリーノートを作成している方には、日付が挿入したノートをDNOから参照できて便利かと思います。
+ +**注意点** +- 動作速度の観点から、バックリンクの表示をオンにする場合、Dataviewプラグインをインストールすることを強くお勧めします。Dataviewプラグインはバックリンクファイルの情報をキャッシュしており、有効化されているとDNOはそのキャッシュ情報を利用するため、処理がずっと速くなります。
+ ### Simple filter / Include / Exclude フィルター 不必要な項目を非表示にし、必要な項目のみ表示するために、simple filter, include, exclude の3つのフィルター機能を実装しています。
simple filterは、指定した単語やフレーズにマッチする項目を、単純に非表示にします。項目ごとの階層は考慮されません。
diff --git a/manifest.json b/manifest.json index ab9df16..39d7da2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-daily-note-outline", "name": "Daily Note Outline", - "version": "1.4.1", + "version": "1.5.0", "minAppVersion": "0.15.0", "description": "Add a custom view which shows outline of multiple daily notes with headings, links, tags and list items", "author": "iiz", diff --git a/package.json b/package.json index d7b08c4..38b7730 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-daily-note-outline", - "version": "1.4.1", + "version": "1.5.0", "description": "Add a custom view which shows outline of multiple daily notes with headings, links, tags and list items", "main": "main.js", "scripts": { diff --git a/src/constructDOM.ts b/src/constructDOM.ts index 35e4d65..9651263 100644 --- a/src/constructDOM.ts +++ b/src/constructDOM.ts @@ -1,6 +1,6 @@ import { App, MarkdownView, Menu, TFile, parseLinktext, setIcon, setTooltip } from "obsidian"; import { FileInfo, OutlineData, GRANULARITY_LIST } from "./main"; -import { addFlag, checkFlag, getSubpathPosition, removeFlag } from "./util"; +import { addFlag, checkFlag, checkListCallouts, getSubpathPosition, removeFlag, shouldDisplayListItem } from "./util"; import { DailyNoteOutlineViewType } from "./view"; // アウトライン描画 @@ -127,7 +127,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData dailyNoteTitleEl.dataset.subinfo = info[i].numOfLines.toString(); break; case 'days': - let basedate = (this.settings.initialSearchType == "backward")? window.moment(): window.moment(this.settings.onset); + let basedate = (this.settings.setBaseDateAsHome == false)? window.moment(): window.moment(this.settings.onset); dailyNoteTitleEl.dataset.subinfo = Math.abs(info[i].date.diff(basedate.startOf(GRANULARITY_LIST[this.activeGranularity]),GRANULARITY_LIST[this.activeGranularity])).toString(); break; case 'dow': @@ -155,8 +155,8 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData //ノートタイトルをクリックしたらそのファイルをopen dailyNoteTitleEl.addEventListener( "click", - (event: MouseEvent) => { - this.app.workspace.getLeaf().openFile(files[i]); + async(event: MouseEvent) => { + await this.app.workspace.getLeaf().openFile(files[i]); }, false ); @@ -239,10 +239,10 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData const linkSubpath = parseLinktext(info[i].frontmatterLinks[j].link).subpath; // 抽出 extract if (this.extractMode == true) { - if (this.extractTask == true || !info[i].frontmatterLinks[j].displayText.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ - continue; - } else { + if (this.extractType == 'noraml' && info[i].frontmatterLinks[j].displayText.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ isExtracted = true; + } else { + continue; } } @@ -257,9 +257,8 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData //クリック時 outlineTitle.addEventListener( "click", - (event: MouseEvent) => { - event.preventDefault(); - this.app.workspace.getLeaf().openFile(files[i]); + async (event: MouseEvent) => { + await this.app.workspace.getLeaf().openFile(files[i]); }, false ); @@ -301,7 +300,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData this.plugin.settings.wordsToExtract = info[i].frontmatterLinks[j].displayText; await this.plugin.saveSettings(); this.extractMode = true; - this.extractTask = false; + this.extractType = 'normal'; this.refreshView(false,false); }) ); @@ -411,6 +410,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData // 現アウトライン要素の種別を取得 const element = data[i][j].typeOfElement; + let displayText = data[i][j].displayText; const linkTarget = (element !== 'link')? null : this.app.metadataCache.getFirstLinkpathDest(parseLinktext(data[i][j]?.link).path, files[i].path); const linkSubpath = (!linkTarget)? undefined : parseLinktext(data[i][j]?.link).subpath; @@ -423,7 +423,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData // 組み入れるワードにマッチするか判定 isIncluded = false; for (const value of this.settings.wordsToInclude){ - if ( (value) && data[i][j].displayText.includes(value)){ + if ( (value) && displayText.includes(value)){ isIncluded = true; if (element == 'heading'){ includeModeHeadingLevel = data[i][j].level; @@ -443,7 +443,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData } else { isExcluded = false; for (const value of this.settings.wordsToExclude[element]){ - if ( (value) && data[i][j].displayText.includes(value)){ + if ( (value) && displayText.includes(value)){ isExcluded = true; excludeType = element; if (element == 'heading'){ @@ -459,15 +459,15 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData } //要素ごとの非表示判定 設定で非表示になっていればスキップ - - if (this.settings.showElements[element] == false){ + if (!data[i][j].task && this.settings.showElements[element] == false){ + continue; + } else if (data[i][j].task && !this.settings.showElements.task){ continue; } - // simple filter 除外ワードにマッチすればスキップ for (const value of this.settings.wordsToIgnore[element]){ - if( (value) && data[i][j].displayText.includes(value)){ + if( (value) && displayText.includes(value)){ continue elementloop; } } @@ -475,12 +475,16 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData //// 抽出 extract if (this.extractMode == true) { - if (this.extractTask == false && !data[i][j].displayText.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ - continue; - } else if (this.extractTask == true && data[i][j].task === void 0){ - continue; - } else { + if (this.extractType == 'normal' && displayText.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ + isExtracted = true; + } else if (this.extractType == 'task' && data[i][j].task !== void 0){ + isExtracted = true; + } else if (this.extractType == 'listCallout' && typeof data[i][j].listCallout == 'number') { isExtracted = true; + } else if (this.extractType == 'time' && data[i][j].time){ + isExtracted = true; + } else { + continue; } } @@ -506,19 +510,10 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData // listItems - + let calloutsIndex = undefined; if (element == 'listItems'){ - // 完了タスク非表示設定であれば完了タスクはスキップ - if (this.settings.hideCompletedTasks == true && data[i][j].task =='x'){ - continue; - // 非タスク非表示設定であれば非タスクはスキップ - } else if (this.settings.taskOnly == true && data[i][j].task === void 0){ + if (shouldDisplayListItem(data[i][j], this.settings, calloutsIndex) == false){ continue; - // 非タスクの通常リストアイテム、または タスクは全表示の設定で無ければレベルに応じてスキップ - } else if (this.settings.allTasks == false || data[i][j].task === void 0){ - if ( (data[i][j].level == 2) || (data[i][j].level ==1 && this.settings.allRootItems == false)){ - continue; - } } } @@ -528,6 +523,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData //中身を設定 const outlineTitle: HTMLElement = outlineEl.createDiv("tree-item-self is-clickable nav-file-title"); + //アイコン icon switch(this.settings.icon[element]){ case 'none': @@ -543,24 +539,41 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData break; } // タスクだった場合アイコン上書き - if (element =='listItems' && data[i][j].task !== void 0){ - if (data[i][j].task == 'x'){ - setIcon(outlineTitle, this.settings.icon.taskDone == 'custom' ? - this.settings.customIcon.taskDone : this.settings.icon.taskDone); - } else { - setIcon(outlineTitle, this.settings.icon.task =='custom' ? - this.settings.customIcon.task : this.settings.icon.task); - } - const customStatus = Object.keys(this.settings.taskIcon).find((customStatus)=> this.settings.taskIcon[customStatus].symbol === data[i][j].task); - if (customStatus) { - setIcon(outlineTitle, this.settings.taskIcon[customStatus].icon); + if (element =='listItems'){ + if (data[i][j].task !== void 0){ + if (data[i][j].task == 'x'){ + setIcon(outlineTitle, this.settings.icon.taskDone == 'custom' ? + this.settings.customIcon.taskDone : this.settings.icon.taskDone); + } else { + setIcon(outlineTitle, this.settings.icon.task =='custom' ? + this.settings.customIcon.task : this.settings.icon.task); + } + const customStatus = Object.keys(this.settings.taskIcon).find((customStatus)=> this.settings.taskIcon[customStatus].symbol === data[i][j].task); + if (customStatus) { + setIcon(outlineTitle, this.settings.taskIcon[customStatus].icon); + } } - // setIcon(outlineTitle, this.settings.taskIcon[Object.keys(this.settings.taskIcon).find((iconName)=> this.settings.taskIcon[iconName] === data[i][j].task)]); + //リストコールアウト,タイムリストへの対応( + let calloutsIndex = data[i][j]?.listCallout; + if (typeof calloutsIndex =='number' ) { + outlineTitle.style.backgroundColor =`RGBA(${this.app.plugins.plugins['obsidian-list-callouts'].settings[calloutsIndex].color},0.15)`; + if (this.app.plugins.plugins['obsidian-list-callouts'].settings[calloutsIndex].hasOwnProperty('icon') && data[i][j].task === void 0){ + setIcon(outlineTitle, this.app.plugins.plugins['obsidian-list-callouts'].settings[calloutsIndex].icon); + displayText = displayText.replace(/^\S+\s/,''); + } + } + if (data[i][j]?.time){ + if (data[i][j].task === void 0){ + setIcon(outlineTitle, this.settings.icon.time =='custom' ? + this.settings.customIcon.time : this.settings.icon.time); + } + } } - + //prefix、インデント let prefix = this.settings.prefix[element]; + // 特定のprefixに対応 if ( element == 'heading'){ switch (this.settings.repeatHeadingPrefix){ case 'level': @@ -571,6 +584,9 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData break; } } + if (element == 'listItems' && data[i][j].time){ + prefix = this.settings.prefix.time; + } let indent: number = 0.5; // 見出しのインデント @@ -597,10 +613,14 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData prefix = prefix + '['+data[i][j].task+'] '; } } - let dispText = this.stripMarkdownSymbol(data[i][j].displayText); - - outlineTitle.createDiv("tree-item-inner nav-file-title-content").setText(prefix + dispText); + displayText = this.stripMarkdownSymbol(displayText); + const outlineTitleContent = outlineTitle.createDiv("tree-item-inner nav-file-title-content"); + outlineTitleContent.setText(prefix + displayText); + // wrapLine trueなら折り返し設定 + if (this.settings.wrapLine){ + outlineTitleContent.classList.add('wrap-line'); + } // インラインプレビュー // リンクとタグは、アウトライン要素のあとに文字列が続く場合その行をプレビュー、そうでなければ次の行をプレビュー if (this.settings.inlinePreview) { @@ -698,7 +718,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData this.plugin.settings.wordsToExtract = data[i][j].displayText; await this.plugin.saveSettings(); this.extractMode = true; - this.extractTask = false; + this.extractType = 'normal'; this.refreshView(false,false,false); }) ); @@ -828,6 +848,11 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData }, false ); + // ツールチッププレビュー + let previewText2: string = info[i].lines.join('\n'); + setTooltip(outlineTitle, previewText2, {classes:['DNO-preview']}); + outlineTitle.dataset.tooltipPosition = this.settings.tooltipPreviewDirection; + outlineTitle.setAttribute('data-tooltip-delay','10'); break; } } @@ -843,18 +868,29 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData // 抽出 extract if (this.extractMode == true) { - if (this.extractTask == true || !info[i].backlinks[j].basename.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ - continue; - } else { + if (this.extractType == 'normal' && info[i].backlinks[j].basename.toLowerCase().includes(this.settings.wordsToExtract.toLowerCase())){ isExtracted = true; + } else { + continue; } } const outlineEl: HTMLElement = dailyNoteChildrenEl.createDiv("tree-item nav-file"); const outlineTitle: HTMLElement = outlineEl.createDiv("tree-item-self is-clickable nav-file-title"); - setIcon(outlineTitle,'links-coming-in'); - + + //アイコン icon + switch(this.settings.icon.backlink){ + case 'none': + break; + case 'custom': + setIcon(outlineTitle, this.settings.customIcon.backlink); + break; + default: + setIcon(outlineTitle, this.settings.icon.backlink); + break; + } + outlineTitle.style.paddingLeft ='0.5em'; outlineTitle.createDiv("tree-item-inner nav-file-title-content").setText(info[i].backlinks[j].basename); @@ -948,18 +984,7 @@ export function drawOutline( files: TFile[], info: FileInfo[], data: OutlineData export function scrollToElement(line: number, col: number, app: App): void { const view = app.workspace.getActiveViewOfType(MarkdownView); if (view) { - view.editor.focus(); - view.editor.setCursor (line, col); - view.editor.scrollIntoView( { - from: { - line: line, - ch:0 - }, - to: { - line: line, - ch:0 - } - }, true); + view.setEphemeralState({line}); } } diff --git a/src/createAndOpenDailyNote.ts b/src/createAndOpenDailyNote.ts index 1086b37..bbf3394 100644 --- a/src/createAndOpenDailyNote.ts +++ b/src/createAndOpenDailyNote.ts @@ -1,6 +1,6 @@ import moment from "moment"; import { TFile, WorkspaceLeaf } from 'obsidian'; -import { createDailyNote, getDailyNote, createPeriodicNote, IGranularity, getDateUID, createQuarterlyNote, createYearlyNote} from "obsidian-daily-notes-interface"; +import { createDailyNote, getDailyNote, createPeriodicNote, IGranularity, getDateUID, createQuarterlyNote, createYearlyNote, getDailyNoteSettings} from "obsidian-daily-notes-interface"; export async function createAndOpenDailyNote( granularity: IGranularity, date: moment, allFiles: Record): Promise { @@ -23,7 +23,6 @@ export async function createAndOpenDailyNote( granularity: IGranularity, date: m }; const dailynote = allFiles[getDateUID(date, granularity)]; - //const dailynote = getDailyNote(date, allFiles); if (!dailynote){ await createFile(); } else { @@ -31,3 +30,4 @@ export async function createAndOpenDailyNote( granularity: IGranularity, date: m } } + diff --git a/src/createAndOpenPeriodicNote.ts b/src/createAndOpenPeriodicNote.ts index 2de051a..82a55a3 100644 --- a/src/createAndOpenPeriodicNote.ts +++ b/src/createAndOpenPeriodicNote.ts @@ -1,5 +1,5 @@ /* -Codes in this file is adapted from Periodic Notes by Liam Cain https://github.com/liamcain/obsidian-periodic-notes +Codes in this file is based on Periodic Notes by Liam Cain https://github.com/liamcain/obsidian-periodic-notes The original work is MIT-licensed. @@ -29,7 +29,7 @@ SOFTWARE. import moment from "moment"; import { App, TFile, WorkspaceLeaf, normalizePath, Notice } from 'obsidian'; -import { IGranularity } from "obsidian-daily-notes-interface"; +import { createPeriodicNote, getDailyNoteSettings, IGranularity } from "obsidian-daily-notes-interface"; import { CalendarSet } from "./periodicNotesTypes"; @@ -53,8 +53,8 @@ export async function createAndOpenPeriodicNote( granularity: IGranularity, date ); if (!file) { // fileを作成 - const config = calendarSet[granularity]; - const format = calendarSet[granularity].format; + const config = calendarSet[granularity]; + const format = calendarSet[granularity].format; const filename = date.format(format ? format : DEFAULT_NOTE_FORMAT[granularity]); const templateContents = await getTemplateContents(window.app, config.templatePath); @@ -64,7 +64,7 @@ export async function createAndOpenPeriodicNote( granularity: IGranularity, date date, format, templateContents - ); + ); const destPath = await getNoteCreationPath(window.app, filename, config); file = await window.app.vault.create(destPath, renderedContents); } @@ -98,130 +98,130 @@ async function getTemplateContents( } function applyTemplateTransformations( -filename: string, -granularity: Granularity, -date: moment, -format: string, -rawTemplateContents: string + filename: string, + granularity: Granularity, + date: moment, + format: string, + rawTemplateContents: string ): string { -let templateContents = rawTemplateContents; - -templateContents = rawTemplateContents - .replace(/{{\s*date\s*}}/gi, filename) - .replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")) - .replace(/{{\s*title\s*}}/gi, filename); - -if (granularity === "day") { - templateContents = templateContents - .replace(/{{\s*yesterday\s*}}/gi, date.clone().subtract(1, "day").format(format)) - .replace(/{{\s*tomorrow\s*}}/gi, date.clone().add(1, "d").format(format)) - .replace( - /{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, - (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { - const now = window.moment(); - const currentDate = date.clone().set({ - hour: now.get("hour"), - minute: now.get("minute"), - second: now.get("second"), - }); - if (calc) { - currentDate.add(parseInt(timeDelta, 10), unit); - } - - if (momentFormat) { - return currentDate.format(momentFormat.substring(1).trim()); - } - return currentDate.format(format); - } - ); -} - -if (granularity === "week") { - templateContents = templateContents.replace( - /{{\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*:(.*?)}}/gi, - (_, dayOfWeek, momentFormat) => { - const day = getDayOfWeekNumericalValue(dayOfWeek); - return date.weekday(day).format(momentFormat.trim()); + let templateContents = rawTemplateContents; + + templateContents = rawTemplateContents + .replace(/{{\s*date\s*}}/gi, filename) + .replace(/{{\s*time\s*}}/gi, window.moment().format("HH:mm")) + .replace(/{{\s*title\s*}}/gi, filename); + + if (granularity === "day") { + templateContents = templateContents + .replace(/{{\s*yesterday\s*}}/gi, date.clone().subtract(1, "day").format(format)) + .replace(/{{\s*tomorrow\s*}}/gi, date.clone().add(1, "d").format(format)) + .replace( + /{{\s*(date|time)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, + (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { + const now = window.moment(); + const currentDate = date.clone().set({ + hour: now.get("hour"), + minute: now.get("minute"), + second: now.get("second"), + }); + if (calc) { + currentDate.add(parseInt(timeDelta, 10), unit); + } + + if (momentFormat) { + return currentDate.format(momentFormat.substring(1).trim()); + } + return currentDate.format(format); + } + ); } - ); -} -if (granularity === "month") { - templateContents = templateContents.replace( - /{{\s*(month)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, - (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { - const now = window.moment(); - const monthStart = date - .clone() - .startOf("month") - .set({ - hour: now.get("hour"), - minute: now.get("minute"), - second: now.get("second"), - }); - if (calc) { - monthStart.add(parseInt(timeDelta, 10), unit); + if (granularity === "week") { + templateContents = templateContents.replace( + /{{\s*(sunday|monday|tuesday|wednesday|thursday|friday|saturday)\s*:(.*?)}}/gi, + (_, dayOfWeek, momentFormat) => { + const day = getDayOfWeekNumericalValue(dayOfWeek); + return date.weekday(day).format(momentFormat.trim()); } - - if (momentFormat) { - return monthStart.format(momentFormat.substring(1).trim()); - } - return monthStart.format(format); + ); } - ); -} - -if (granularity === "quarter") { - templateContents = templateContents.replace( - /{{\s*(quarter)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, - (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { - const now = window.moment(); - const monthStart = date - .clone() - .startOf("quarter") - .set({ - hour: now.get("hour"), - minute: now.get("minute"), - second: now.get("second"), - }); - if (calc) { - monthStart.add(parseInt(timeDelta, 10), unit); - } - if (momentFormat) { - return monthStart.format(momentFormat.substring(1).trim()); + if (granularity === "month") { + templateContents = templateContents.replace( + /{{\s*(month)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, + (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { + const now = window.moment(); + const monthStart = date + .clone() + .startOf("month") + .set({ + hour: now.get("hour"), + minute: now.get("minute"), + second: now.get("second"), + }); + if (calc) { + monthStart.add(parseInt(timeDelta, 10), unit); + } + + if (momentFormat) { + return monthStart.format(momentFormat.substring(1).trim()); + } + return monthStart.format(format); } - return monthStart.format(format); + ); } - ); -} -if (granularity === "year") { - templateContents = templateContents.replace( - /{{\s*(year)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, - (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { - const now = window.moment(); - const monthStart = date - .clone() - .startOf("year") - .set({ - hour: now.get("hour"), - minute: now.get("minute"), - second: now.get("second"), - }); - if (calc) { - monthStart.add(parseInt(timeDelta, 10), unit); + if (granularity === "quarter") { + templateContents = templateContents.replace( + /{{\s*(quarter)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, + (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { + const now = window.moment(); + const monthStart = date + .clone() + .startOf("quarter") + .set({ + hour: now.get("hour"), + minute: now.get("minute"), + second: now.get("second"), + }); + if (calc) { + monthStart.add(parseInt(timeDelta, 10), unit); + } + + if (momentFormat) { + return monthStart.format(momentFormat.substring(1).trim()); + } + return monthStart.format(format); } + ); + } - if (momentFormat) { - return monthStart.format(momentFormat.substring(1).trim()); + if (granularity === "year") { + templateContents = templateContents.replace( + /{{\s*(year)\s*(([+-]\d+)([yqmwdhs]))?\s*(:.+?)?}}/gi, + (_, _timeOrDate, calc, timeDelta, unit, momentFormat) => { + const now = window.moment(); + const monthStart = date + .clone() + .startOf("year") + .set({ + hour: now.get("hour"), + minute: now.get("minute"), + second: now.get("second"), + }); + if (calc) { + monthStart.add(parseInt(timeDelta, 10), unit); + } + + if (momentFormat) { + return monthStart.format(momentFormat.substring(1).trim()); + } + return monthStart.format(format); } - return monthStart.format(format); + ); } - ); -} -return templateContents; + return templateContents; } @@ -229,14 +229,14 @@ async function getNoteCreationPath( app: App, filename: string, periodicConfig: PeriodicConfig - ): Promise { + ): Promise { const directory = periodicConfig.folder ?? ""; const filenameWithExt = !filename.endsWith(".md") ? `${filename}.md` : filename; - + const path = normalizePath(join(directory, filenameWithExt)); await ensureFolderExists(app, path); return path; - } + } // Credit: @creationix/path.js function join(...partSegments: string[]): string { @@ -280,23 +280,80 @@ function getDaysOfWeek(): string[] { // eslint-disable-next-line @typescript-eslint/no-explicit-any let weekStart = (moment.localeData() as any)._week.dow; const daysOfWeek = [ - "sunday", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", ]; - + while (weekStart) { - const day = daysOfWeek.shift(); - if (day) daysOfWeek.push(day); - weekStart--; + const day = daysOfWeek.shift(); + if (day) daysOfWeek.push(day); + weekStart--; } return daysOfWeek; - } +} function getDayOfWeekNumericalValue(dayOfWeekName: string): number { return getDaysOfWeek().indexOf(dayOfWeekName.toLowerCase()); - } \ No newline at end of file +} + + +// The following code is based on Daily Note Interface Notes by Liam Cain +// デイリーノートまたはPeriodic notes v0.xにおいて、指定した日付のデイリーノートを作成 +export async function createDailyNoteForUnresolvedLink(app: App, date: moment){ + if (date.isSameOrAfter(window.moment(),'day')){ + // 日付が本日か未来であればDailyNoteInterfaceから普通にデイリーノートを作成 + await createPeriodicNote('day', date); + } else { + // 日付が過去であればテンプレートを適用せずに空ノートをつくる + const {format, folder} = getDailyNoteSettings(); + const filename = date.format(format); + const filenameWithExt = !filename.endsWith(".md") ? `${filename}.md` : filename; + + const path = normalizePath(join(folder ?? "", filenameWithExt)); + await ensureFolderExists(app, path); + + try { + await this.app.vault.create(path,""); + } catch(e){ + console.error('Daily Note Outline: Could not create ',path, e.message); + } + } + +} + +// Periodic notes v1.xにおいて、指定した日付のデイリーノートを作成 +// *注意点*:この関数は過去の日付に対してはテンプレートを適用せず、空ノートを作ろうとするが、 +// Periodic notes 1.0.0-beta3では、デイリーノートのファイル名フォーマットに合致する新規ノートが作成されたのを検出した場合、 +// Periodic notes側でテンプレートを強制適用する処理がなされている。そのため、テンプレートが指定されていると空ノートにならない。 +export async function createDailyNoteForUnresolvedLinkPNbeta(app: App, date: moment, calendarSet: CalendarSet){ + + const isTodayOrFuture = Boolean(date.isSameOrAfter(window.moment(),'day')); + const config = calendarSet.day; + const format = calendarSet.day.format; + + const filename = date.format(format ? format : DEFAULT_NOTE_FORMAT.day); + + let templateContents = ""; + let renderedContents = ""; + if (isTodayOrFuture){ + templateContents = await getTemplateContents(app, config.templatePath); + renderedContents = applyTemplateTransformations( + filename, + 'day', + date, + format, + templateContents + ); + } + const destPath = await getNoteCreationPath(app, filename, config); + try { + await this.app.vault.create(destPath, renderedContents); + } catch(e) { + console.error('Daily Note Outline: Could not create', destPath, e.message); + } +} diff --git a/src/drawUI.ts b/src/drawUI.ts index 1d48847..136a873 100644 --- a/src/drawUI.ts +++ b/src/drawUI.ts @@ -1,11 +1,13 @@ -import { setIcon, TFile, Menu } from 'obsidian'; +import { setIcon, TFile, Menu, Notice } from 'obsidian'; import { GRANULARITY_LIST, GRANULARITY_TO_PERIODICITY, FILEINFO_TO_DISPLAY, FILEINFO_TO_DISPLAY_DAY,DAYS_PER_UNIT, } from './main'; import { createAndOpenDailyNote } from 'src/createAndOpenDailyNote'; import { ModalExtract } from 'src/modalExtract'; import {type CalendarSet,} from 'src/periodicNotesTypes'; import { createAndOpenPeriodicNote } from './createAndOpenPeriodicNote'; import { IGranularity } from 'obsidian-daily-notes-interface'; +import { ModalJump } from './modalJump'; +import { searchFirstNote } from './util'; export function drawUI(): void { @@ -25,41 +27,143 @@ export function drawUI(): void { uiExtract.call(this, navButtonContainer); uiCollapse.call(this, navButtonContainer); - + // 日付の範囲の描画 const navDateRange: HTMLElement = navHeader.createDiv("nav-date-range"); let latest = this.searchRange.latest.clone(); let earliest = this.searchRange.earliest.clone(); - if (this.settings.initialSearchType =='backward'){ - earliest = latest.clone().subtract(this.settings.duration[granularity] - 1 ,granularity); - if (latest.isSame(window.moment(),"day")){ - latest = latest.add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); - } - } - if (this.settings.initialSearchType =='forward'){ - latest = earliest.clone().add(this.settings.duration[granularity] -1 ,granularity); + // forward search 廃止に伴い変更 + earliest = latest.clone().subtract(this.settings.duration[granularity] - 1 ,granularity); + if (latest.isSame(window.moment(),"day")){ + latest = latest.clone().add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); } const dateRange: string = earliest.format("YYYY/MM/DD [-] ") + latest.format( earliest.isSame(latest,'year') ? "MM/DD": "YYYY/MM/DD"); navDateRange.createDiv("nav-date-range-content").setText(dateRange); - //日付範囲クリック時の動作 後で変更する + //日付範囲クリック時の動作 navDateRange.addEventListener( "click", async (event:MouseEvent) =>{ - event.preventDefault(); - const onsetDate = window.moment(this.settings.onset,"YYYY-MM-DD"); - if (onsetDate.isValid()){ - this.searchRange.earliest = onsetDate; - this.searchRange.latest = onsetDate.clone().add(this.settings.duration.day -1,'days'); - } else { - // onsetDateが不正なら当日起点のbackward searchを行う - this.searchRange.latest = window.moment().startOf('day'); - this.searchRange.earliest = window.moment().startOf('day').subtract(this.settings.duration.day - 1,'days') + const onSubmit = (enteredDate: string) => { + const targetDate = window.moment(enteredDate); + if (targetDate.isValid()){ + this.searchRange.latest = targetDate; + this.searchRange.earliest = targetDate.clone().subtract(this.settings.duration.day -1,'days'); + this.refreshView(false, true, true); + } else { + new Notice("entered date is invalid."); + } } - this.refreshView(false, true, true); + new ModalJump(this.app, this.plugin, onSubmit).open(); + + } + ); + navDateRange.addEventListener( + "contextmenu", + (event: MouseEvent) =>{ + const menu = new Menu(); + menu.addItem((item) => + item.setTitle(`Jump to ${this.settings.onset}`) + .onClick(()=>{ + const targetDate = window.moment(this.settings.onset,"YYYY-MM-DD"); + if (targetDate.isValid()){ + this.searchRange.latest = targetDate; + this.searchRange.earliest = targetDate.clone().subtract(this.settings.duration.day -1,'days'); + } else { + new Notice(`${this.settings.onset} is an invalid date.`); + } + this.refreshView(false, true, true); + }) + ); + menu.addSeparator(); + menu.addItem((item) => + item.setTitle("Jump to 3 months ago") + .onClick(() =>{ + this.searchRange.latest.subtract(3,'months'); + this.searchRange.earliest.subtract(3,'months'); + this.refreshView(false,true,true); + }) + ); + menu.addItem((item) => + item.setTitle("Jump to 6 months ago") + .onClick(() =>{ + this.searchRange.latest.subtract(6,'months'); + this.searchRange.earliest.subtract(6,'months'); + this.refreshView(false,true,true); + }) + ); + menu.addItem((item) => + item.setTitle("Jump to 1 year ago") + .onClick(() =>{ + this.searchRange.latest.subtract(12,'months'); + this.searchRange.earliest.subtract(12,'months'); + this.refreshView(false,true,true); + }) + ); + menu.addItem((item) => + item.setTitle("Jump to 2 years ago") + .onClick(() =>{ + this.searchRange.latest.subtract(24,'months'); + this.searchRange.earliest.subtract(24,'months'); + this.refreshView(false,true,true); + }) + ); + if (window.moment().subtract(3,'months').isSameOrAfter(this.searchRange.latest)){ + menu.addSeparator(); + menu.addItem((item) => + item.setTitle("Jump to 3 months later") + .onClick(() =>{ + this.searchRange.latest.add(3,'months'); + this.searchRange.earliest.add(3,'months'); + this.refreshView(false,true,true); + }) + ); + } + if (window.moment().subtract(6,'months').isSameOrAfter(this.searchRange.latest)){ + menu.addItem((item) => + item.setTitle("Jump to 6 months later") + .onClick(() =>{ + this.searchRange.latest.add(6,'months'); + this.searchRange.earliest.add(6,'months'); + this.refreshView(false,true,true); + }) + ); + } + if (window.moment().subtract(12,'months').isSameOrAfter(this.searchRange.latest)){ + menu.addItem((item) => + item.setTitle("Jump to 1 year later") + .onClick(() =>{ + this.searchRange.latest.add(12,'months'); + this.searchRange.earliest.add(12,'months'); + this.refreshView(false,true,true); + }) + ); + } + if (window.moment().subtract(24,'months').isSameOrAfter(this.searchRange.latest)){ + menu.addItem((item) => + item.setTitle("Jump to 2 years later") + .onClick(() =>{ + this.searchRange.latest.add(24,'months'); + this.searchRange.earliest.add(24,'months'); + this.refreshView(false,true,true); + }) + ); + } + menu.addSeparator(); + menu.addItem((item) => + item.setTitle("Jump to the first note") + .onClick(() =>{ + const targetDate = searchFirstNote.call(this); + this.searchRange.latest = targetDate; + this.searchRange.earliest = targetDate.clone().subtract(this.settings.duration.day -1,'days'); + this.refreshView(false,true,true); + }) + ); + + menu.showAtMouseEvent(event); } ); @@ -228,7 +332,6 @@ function uiUpdate (parentEl: HTMLElement):void { navActionButton.addEventListener( "click", async (event:MouseEvent) =>{ - // this.resetSearchRange(); this.refreshView(true, true, true); } ); @@ -263,19 +366,19 @@ function uiSetting(parentEl: HTMLElement, granularity: IGranularity):void{ }) ); } - if (this.settings.showElements.listItems){ - const icon = (this.settings.taskOnly)? "check": ""; - menu.addItem((item)=> - item - .setTitle("tasks only") - .setIcon(icon) - .onClick(async()=>{ - this.settings.taskOnly = !this.settings.taskOnly; - await this.plugin.saveSettings(); - this.refreshView(false,false,false); - }) - ); - } + // if (this.settings.showElements.listItems){ + // const icon = (this.settings.taskOnly)? "check": ""; + // menu.addItem((item)=> + // item + // .setTitle("tasks only") + // .setIcon(icon) + // .onClick(async()=>{ + // this.settings.taskOnly = !this.settings.taskOnly; + // await this.plugin.saveSettings(); + // this.refreshView(false,false,false); + // }) + // ); + // } const iconBacklink = (this.settings.showBacklinks == true)? "check":""; menu.addItem((item)=> @@ -378,7 +481,6 @@ function uiCreateDailyNote(parentEl:HTMLElement, granularity: IGranularity):void const menu = new Menu(); menu.addItem((item) => item - // .setTitle("create/open tomorrow's daily note") .setTitle(labelForNext) .setIcon("calendar-plus") .onClick(async ()=> { @@ -410,6 +512,7 @@ function uiExtract(parentEl: HTMLElement):void{ const onSubmit = (enableExtract: boolean) => { if (enableExtract){ this.extractMode = true; + this.extractType = 'normal'; this.refreshView(false,false,false); } } @@ -425,7 +528,7 @@ function uiExtract(parentEl: HTMLElement):void{ "click", async (event:MouseEvent) =>{ this.extractMode = false; - this.extractTask = false; + this.extractType = 'normal'; this.refreshView(false,false,false); }); @@ -440,7 +543,28 @@ function uiExtract(parentEl: HTMLElement):void{ .setIcon("check-square") .onClick(async ()=> { this.extractMode = true; - this.extractTask = true; + this.extractType = 'task'; + this.refreshView(false,false,false); + }) + ); + + menu.addItem((item) => + item + .setTitle("extract list callouts") + .setIcon("lightbulb") + .onClick(async ()=> { + this.extractMode = true; + this.extractType = 'listCallout'; + this.refreshView(false,false,false); + }) + ); + menu.addItem((item) => + item + .setTitle("extract time lists") + .setIcon("clock") + .onClick(async ()=> { + this.extractMode = true; + this.extractType = 'time'; this.refreshView(false,false,false); }) ); diff --git a/src/getOutline.ts b/src/getOutline.ts index 7f77adc..dc218e5 100644 --- a/src/getOutline.ts +++ b/src/getOutline.ts @@ -2,6 +2,7 @@ import { TFile } from "obsidian"; import { getDateFromFile } from "obsidian-daily-notes-interface"; import { FileInfo, GRANULARITY_LIST, OutlineData } from "./main"; import { getBacklinkFilesDataview } from "./getTargetFiles"; +import { checkListCallouts, checkTimeList } from "./util"; // デイリーノートの配列から各ファイルに関する情報を抽出 export async function getFileInfo(files:TFile[]):Promise{ @@ -76,11 +77,29 @@ export async function getOutline(files:TFile[],info:FileInfo[]):Promise, granularity: IGran let checkDate = this.searchRange.latest.clone(); let checkDateEarliest = this.searchRange.earliest.clone(); - if (this.settings.initialSearchType =='backward'){ - if(checkDate.isSame(window.moment(),'day')){ - checkDate.add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); - } - checkDateEarliest = this.searchRange.latest.clone().subtract(this.settings.duration[granularity] - 1,granularity); - } else { - // forward searchのとき - checkDate = this.searchRange.earliest.clone().add(this.settings.duration[granularity] - 1,granularity); + if(checkDate.isSame(window.moment(),'day')){ + checkDate.add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); } + checkDateEarliest = this.searchRange.latest.clone().subtract(this.settings.duration[granularity] - 1,granularity); while (checkDate.isSameOrAfter(checkDateEarliest,granularity)){ if (allFiles[getDateUID(checkDate, granularity)]){ @@ -38,16 +33,12 @@ export function getTargetPeriodicNotes(calendarSet: CalendarSet, granularity: IG // day基準の探索範囲であるsearchRangeを元に、粒度に応じて探索範囲を拡張したものをcheckDate,checkDateEarliestに代入 let checkDate = this.searchRange.latest.clone(); let checkDateEarliest = this.searchRange.earliest.clone(); - - if (this.settings.initialSearchType=='backward'){ - if(checkDate.isSame(window.moment(),'day')){ - checkDate.add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); - } - checkDateEarliest = this.searchRange.latest.clone().subtract(this.settings.duration[granularity] - 1,granularity); - } else { - // forward searchのとき - checkDate = this.searchRange.earliest.clone().add(this.settings.duration[granularity] - 1,granularity); + + if(checkDate.isSame(window.moment(),'day')){ + checkDate.add(Math.ceil(this.settings.offset/DAYS_PER_UNIT[granularity]),granularity); } + checkDateEarliest = this.searchRange.latest.clone().subtract(this.settings.duration[granularity] - 1,granularity); + if (this.settings.showDebugInfo){ console.log('DNO:searching caches of Periodic Notes... calendar set:',calendarSet); } @@ -111,16 +102,4 @@ export function getBacklinkFilesDataview(app:App, file:TFile):TFile[]{ } } return files; -} - -export function getDateInfo(app:App):DateInfo[] { - - // 粒度はdayに固定。 - let startDate = this.searchRange.latest.clone(); - if (this.settings.initialSearchType =='backward' && startDate.isSame(window.moment(),'day')){ - startDate.add(this.settings.offset,'days'); // 表記合ってる? - } - - - } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 5692657..ca038e9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,12 +7,14 @@ import moment from "moment"; import { DailyNoteOutlineView, DailyNoteOutlineViewType } from 'src/view' import { DailyNoteOutlineSettingTab } from 'src/setting' +import { time } from 'console'; +import { checkUnresolvedLinks } from './util'; // 設定項目 export interface DailyNoteOutlineSettings { - initialSearchType: string; //forward or backward 特定日から前方探索or当日から後方探索 + //initialSearchType: string; //forward or backward 特定日から前方探索or当日から後方探索 offset: number; // 未来の日付も含む場合何日分含めるか number of future days to show onset: string; // 特定日からforwardに探索する場合の起点日 onset date duration:{ @@ -26,7 +28,8 @@ export interface DailyNoteOutlineSettings { heading: boolean; link: boolean; tag: boolean; - listItems: boolean + listItems: boolean; + task: boolean, }; headingLevel: boolean[]; allRootItems: boolean; @@ -75,6 +78,8 @@ export interface DailyNoteOutlineSettings { note: string; task: string; taskDone: string; + backlink: string; + time: string; }; customIcon:{ @@ -85,6 +90,8 @@ export interface DailyNoteOutlineSettings { note: string; task: string; taskDone: string; + backlink: string; + time: string; }; indent:{ @@ -101,6 +108,8 @@ export interface DailyNoteOutlineSettings { listItems: string; task: string; taskDone: string; + backlink: string; + time: string; }; repeatHeadingPrefix: string; // none, level, level-1 addCheckboxText: boolean; @@ -161,11 +170,20 @@ export interface DailyNoteOutlineSettings { } popoutAlwaysOnTop: boolean; + showListCallouts: boolean; + showTimeList: boolean; + + setBaseDateAsHome: boolean; + + checkUnresolvedLinksAtStartup: boolean; + + wrapLine: boolean; + bootDelayTime: number; } // 設定項目デフォルト export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { - initialSearchType: 'backward', + //initialSearchType: 'backward', offset: 7, onset: '2020-03-30', duration: { @@ -179,7 +197,8 @@ export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { heading: true, link: true, tag: true, - listItems: true + listItems: true, + task: true, }, headingLevel: [true, true, true, false, false, false], @@ -227,7 +246,9 @@ export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { listItems: 'list', note: 'file', task: 'square', - taskDone: 'check-square' + taskDone: 'check-square', + backlink: 'links-coming-in', + time: 'clock' }, customIcon:{ heading: 'hash', @@ -236,7 +257,9 @@ export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { listItems: 'list', note:'file', task:'square', - taskDone:'check-square' + taskDone:'check-square', + backlink:'links-coming-in', + time: 'clock' }, indent:{ @@ -251,7 +274,9 @@ export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { tag: '', listItems: '', task: '', - taskDone: '' + taskDone: '', + backlink:'', + time: '' }, repeatHeadingPrefix:'none', addCheckboxText: false, @@ -325,6 +350,17 @@ export const DEFAULT_SETTINGS: DailyNoteOutlineSettings = { height: 800 }, popoutAlwaysOnTop: false, + + showListCallouts: true, + showTimeList: true, + + setBaseDateAsHome: false, + + checkUnresolvedLinksAtStartup: false, + + wrapLine: true, + + bootDelayTime: 300, } // export interface FileStatus { @@ -357,13 +393,15 @@ export interface DateInfo { export interface OutlineData { - typeOfElement:'heading'|'link'|'tag'|'listItems'; + typeOfElement:'heading'|'link'|'tag'|'listItems'|'task'; position:Pos; link?:string; displayText?: string; // level :listItemsについては0:トップ、1:ルート、2:それ以下 level?:number; task?: string|undefined; + listCallout?: number|null; + time?: string|null; } export const DAYS_PER_UNIT ={ @@ -426,6 +464,14 @@ export default class DailyNoteOutlinePlugin extends Plugin { } }); + this.addCommand({ + id: 'create-dailynote-for-unresolved-links', + name: 'Create daily notes for unresolved links', + callback: async()=>{ + checkUnresolvedLinks.call(this.view); + + } + }) // // get Today's Note: DailyNoteInterfaceのtestのための開発用コマンド。 @@ -484,6 +530,20 @@ export default class DailyNoteOutlinePlugin extends Plugin { if (typeof this.settings.duration === "number"){ this.settings.duration = DEFAULT_SETTINGS.duration; } + // v1.4.1→v1.5でsettings.icon(customIcon).backlink/time / showElements.taskを追加したことへの対応 + if (!this.settings.icon.hasOwnProperty('backlink')){ + this.settings.icon.backlink = DEFAULT_SETTINGS.icon.backlink; + this.settings.customIcon.backlink = DEFAULT_SETTINGS.customIcon.backlink; + this.settings.prefix.backlink = DEFAULT_SETTINGS.prefix.backlink; + } + if (!this.settings.icon.hasOwnProperty('time')){ + this.settings.icon.time = DEFAULT_SETTINGS.icon.time; + this.settings.customIcon.time = DEFAULT_SETTINGS.customIcon.time; + this.settings.prefix.time = DEFAULT_SETTINGS.prefix.time; + } + if (!this.settings.showElements.hasOwnProperty('task')){ + this.settings.showElements.task = DEFAULT_SETTINGS.showElements.task; + } } async saveSettings() { @@ -517,6 +577,5 @@ export default class DailyNoteOutlinePlugin extends Plugin { if (activateView) { this.app.workspace.revealLeaf(leaf); } - } - + } } \ No newline at end of file diff --git a/src/modalExtract.ts b/src/modalExtract.ts index d1c06a8..6ffa79f 100644 --- a/src/modalExtract.ts +++ b/src/modalExtract.ts @@ -46,7 +46,7 @@ export class ModalExtract extends Modal { .onClick(() => { this.close(); })); - // Enterでの入力に後で対応したい + // Enterでの入力 this.scope.register([], 'Enter', (evt: KeyboardEvent)=>{ this.executeExtract(); diff --git a/src/modalJump.ts b/src/modalJump.ts new file mode 100644 index 0000000..1260930 --- /dev/null +++ b/src/modalJump.ts @@ -0,0 +1,66 @@ + +import { App, Modal, Scope, Setting } from 'obsidian'; +import DailyNoteOutlinePlugin from 'src/main'; +export class ModalJump extends Modal { + plugin: DailyNoteOutlinePlugin; + scope: Scope; + + inputedValue: string; + enableExtract : boolean; + onSubmit: (enteredDate: string) => void; + + constructor(app: App, plugin: DailyNoteOutlinePlugin, onSubmit: (enteredDate: string) => void) { + super(app); + this.plugin = plugin; + this.onSubmit = onSubmit; + } + + onOpen() { + this.inputedValue = this.plugin.settings.onset; + + const { contentEl } = this; + contentEl.createEl("br"); + + new Setting(contentEl) + .setName("Enter date to jump(YYYY-MM-DD):") + .addText((text) =>{ + text.setValue(this.plugin.settings.onset).inputEl.select(); + text.onChange((value) => { + this.inputedValue = value + }); + }); + + new Setting(contentEl) + .addButton((btn) => + btn + .setButtonText("Jump") + .setCta() + .onClick( + async () => { + this.executeJump(); + } + )) + .addButton((btn) => + btn + .setButtonText("Cancel") + .onClick(() => { + this.close(); + })); + // Enterでの入力 + this.scope.register([], 'Enter', + (evt: KeyboardEvent)=>{ + this.executeJump(); + } + ); + } + + onClose() { + let { contentEl } = this; + contentEl.empty(); + } + + async executeJump():Promise{ + this.close(); + this.onSubmit(this.inputedValue); + } +} diff --git a/src/setting.ts b/src/setting.ts index 12611fd..d557b1c 100644 --- a/src/setting.ts +++ b/src/setting.ts @@ -27,28 +27,6 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }); } - this.containerEl.createEl("h4", { - text: "Basics", - cls: 'setting-category' - }); - - new Setting(containerEl) - .setName("Initial search type") - .setDesc("search backward from today / forward from a specific date") - .addDropdown((dropdown) => { - dropdown - .addOption("backward", "backward") - .addOption("forward","forward") - .setValue(this.plugin.settings.initialSearchType) - .onChange(async (value) => { - this.plugin.settings.initialSearchType = value; - this.display(); - await this.plugin.saveSettings(); - this.plugin.view.resetSearchRange(); - this.plugin.view.refreshView(false,true,true); - }) - }); - new Setting(containerEl) .setName("Search duration") .setDesc("number of days to search per page (default = 28)") @@ -97,9 +75,8 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }); new Setting(containerEl) - .setName("Onset date") - .setClass('setting-indent') - .setDesc("onset date in forward search (YYYY-MM-DD)") + .setName("Base date") + .setDesc("You can set one base date to use for jumping back(YYYY-MM-DD)") .addText((text) => { text .setPlaceholder(DEFAULT_SETTINGS.onset) @@ -118,11 +95,25 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { } else { this.containerEl.createEl("h4", { text: "invalid date", - }); + }); } */ }) - }); + }); + + new Setting(containerEl) + .setName("Set Base date as Home") + .setClass('setting-indent') + .setDesc("If enabled, daily notes created on base date are displayed in the view at startup or when Home icon is clicked.") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.setBaseDateAsHome) + .onChange(async (value) => { + this.plugin.settings.setBaseDateAsHome = value; + this.display(); + await this.plugin.saveSettings(); + }) + }); new Setting(containerEl) @@ -167,7 +158,7 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }); new Setting(containerEl) - .setName("Show list items & tasks") + .setName("Show list items") .addToggle((toggle) => { toggle .setValue(this.plugin.settings.showElements.listItems) @@ -195,34 +186,56 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }) }); + new Setting(containerEl) - .setName("Show all tasks") - .setDesc("show all task items regardless of their level") + .setName("Show all list callouts") + .setDesc("shows all list items marked with List Callouts plugin") .setClass('setting-indent') .addToggle((toggle) => { toggle - .setValue(this.plugin.settings.allTasks) + .setValue(this.plugin.settings.showListCallouts) .onChange(async (value) => { - this.plugin.settings.allTasks = value; + this.plugin.settings.showListCallouts = value; this.display(); await this.plugin.saveSettings(); this.plugin.view.refreshView(false,false,true); }) + }); + new Setting(containerEl) - .setName("Task only") - .setDesc("if enabled, normal list items are hidden") + .setName("Show all time lists") + .setDesc("shows all list items start with time(- HH:mm )") .setClass('setting-indent') .addToggle((toggle) => { toggle - .setValue(this.plugin.settings.taskOnly) + .setValue(this.plugin.settings.showTimeList) .onChange(async (value) => { - this.plugin.settings.taskOnly = value; + this.plugin.settings.showTimeList = value; this.display(); await this.plugin.saveSettings(); this.plugin.view.refreshView(false,false,true); }) + }); + + } + + new Setting(containerEl) + .setName("Show tasks") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showElements.task) + .onChange(async (value) => { + this.plugin.settings.showElements.task = value; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }) + + }); + + if (this.plugin.settings.showElements.task){ new Setting(containerEl) .setName("Hide completed tasks") .setClass('setting-indent') @@ -237,7 +250,7 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }) }); } - + new Setting(containerEl) .setName("Show links in properties") .addToggle((toggle) => { @@ -860,9 +873,25 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { cls: 'setting-category' }); + // 要素名の折り返し + new Setting(containerEl) + .setName("Wrap outline element text") + .setDesc("If enabled, long element names are displayed wrapped on multiple lines") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.wrapLine) + .onChange(async (value) => { + this.plugin.settings.wrapLine = value; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }) + + }); + // headingのインデントレベルにあわせて他の要素をインデントするか new Setting(containerEl) - .setName("Indent other than headings") + .setName("Indentation of outline elements (other than headings)") .setDesc("Whether other elements should be indented to preceding headings") .addDropdown((dropdown) => { dropdown @@ -1291,6 +1320,67 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { this.plugin.view.refreshView(false,false,true); }; }); + + //タイムリスト + this.containerEl.createEl("p", { + text: "Time lists", + cls: 'setting-category' + }); + new Setting(containerEl) + .setName("Icon") + .setClass("setting-indent") + .addDropdown((dropdown) => { + dropdown + .addOption("none", "none") + .addOption("clock","clock") + .addOption("message-square","message-square") + .addOption("message-circle","message-circle") + .addOption("twitter","twitter") + .addOption("custom","custom") + .setValue(this.plugin.settings.icon.time) + .onChange(async (value) => { + this.plugin.settings.icon.time = value; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }) + }); + + if (this.plugin.settings.icon.time == 'custom'){ + new Setting(containerEl) + .setName("Custom icon") + .setClass("setting-indent-2") + .setDesc("enter Lucide Icon name") + .addText((text) => { + text.inputEl.setAttr('type','string'); + text + .setPlaceholder(DEFAULT_SETTINGS.customIcon.time) + .setValue(this.plugin.settings.customIcon.time); + text.inputEl.onblur = async (e: FocusEvent) => { + const inputedValue = (e.target as HTMLInputElement).value; + this.plugin.settings.customIcon.time = inputedValue; + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }; + }); + } + + new Setting(containerEl) + .setName("Prefix") + .setClass("setting-indent") + .addText((text) => { + text.inputEl.setAttr('type','string'); + text + .setPlaceholder(DEFAULT_SETTINGS.prefix.time) + .setValue(this.plugin.settings.prefix.time); + text.inputEl.onblur = async (e: FocusEvent) => { + const inputedValue = (e.target as HTMLInputElement).value; + this.plugin.settings.prefix.time = inputedValue; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }; + }); //未完了タスク this.containerEl.createEl("p", { @@ -1428,6 +1518,69 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { } + // バックリンク + if (this.plugin.settings.showBacklinks){ + this.containerEl.createEl("p", { + text: "Backlinks", + cls: 'setting-category' + }); + new Setting(containerEl) + .setName("Icon") + .setClass("setting-indent") + .addDropdown((dropdown) => { + dropdown + .addOption("none", "none") + .addOption("links-coming-in","links-coming-in") + .addOption("file","file") + .addOption("corner-up-left","corner-up-left") + .addOption("clock","clock") + .addOption("custom","custom") + .setValue(this.plugin.settings.icon.backlink) + .onChange(async (value) => { + this.plugin.settings.icon.backlink = value; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }) + }); + + if (this.plugin.settings.icon.backlink == 'custom'){ + new Setting(containerEl) + .setName("Custom icon") + .setClass("setting-indent-2") + .setDesc("enter Lucide Icon name") + .addText((text) => { + text.inputEl.setAttr('type','string'); + text + .setPlaceholder(DEFAULT_SETTINGS.customIcon.backlink) + .setValue(this.plugin.settings.customIcon.backlink); + text.inputEl.onblur = async (e: FocusEvent) => { + const inputedValue = (e.target as HTMLInputElement).value; + this.plugin.settings.customIcon.backlink = inputedValue; + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }; + }); + } + + new Setting(containerEl) + .setName("Prefix") + .setClass("setting-indent") + .addText((text) => { + text.inputEl.setAttr('type','string'); + text + .setPlaceholder(DEFAULT_SETTINGS.prefix.backlink) + .setValue(this.plugin.settings.prefix.backlink); + text.inputEl.onblur = async (e: FocusEvent) => { + const inputedValue = (e.target as HTMLInputElement).value; + this.plugin.settings.prefix.backlink = inputedValue; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(false,false,true); + }; + }); + } + this.containerEl.createEl("h4", { text: "Others", @@ -1462,6 +1615,41 @@ export class DailyNoteOutlineSettingTab extends PluginSettingTab { }) }); + if (this.plugin.settings.showBacklinks){ + new Setting(containerEl) + .setName("Create daily notes corresponding to unresolved links at startup") + .setDesc("To detect backlinks to daily notes, at startup, the plugin searches for links to dates and creates the corresponding daily note if one has not been created.") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.checkUnresolvedLinksAtStartup) + .onChange(async (value) => { + this.plugin.settings.checkUnresolvedLinksAtStartup = value; + this.display(); + await this.plugin.saveSettings(); + this.plugin.view.refreshView(true,true,true); + }) + }); + + } + + new Setting(containerEl) + .setName("Startup delay time(ms)") + .setDesc("Wait for the specified time at startup. If daily notes fail to load at startup, increasing this may resolve the issue. (default = 300)") + .addText((text) => { + text.inputEl.setAttr('type','number'); + text + .setPlaceholder(String(DEFAULT_SETTINGS.bootDelayTime)) + .setValue(String(this.plugin.settings.bootDelayTime)) + + text.inputEl.onblur = async (e: FocusEvent) => { + let parsed = parseInt((e.target as HTMLInputElement).value,10); + if (parsed <= 0 || parsed >=200000){ + parsed = DEFAULT_SETTINGS.bootDelayTime + } + this.plugin.settings.bootDelayTime = parsed; + await this.plugin.saveSettings(); + } + }); this.containerEl.createEl("h4", { text: "Debug", diff --git a/src/util.ts b/src/util.ts index 3d6ea74..42e728c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,9 @@ -import { App, Pos, TFile } from "obsidian"; -import { DailyNoteOutlineSettings } from "./main"; +import { App, Notice, Pos, TFile } from "obsidian"; +import { getDateFromFile, getDateFromPath } from "obsidian-daily-notes-interface"; +import { DailyNoteOutlineSettings, GRANULARITY_LIST } from "./main"; + +import moment from "moment"; +import { createDailyNoteForUnresolvedLink, createDailyNoteForUnresolvedLinkPNbeta } from "./createAndOpenPeriodicNote"; // data.jsonのfileFlagを掃除:値が空配列のプロパティを削除 export function cleanFileFlag(file:TFile, settings:DailyNoteOutlineSettings): void { @@ -56,4 +60,138 @@ export function getSubpathPosition (app:App, file:TFile, subpath:string):Pos|nul } } return null; -} \ No newline at end of file +} + +// list calloutのチェック +export function checkListCallouts (checkString: string, callouts){ + if (!callouts) return null; + for (let i=0; i{ + if ( this.settings.exactMatchOnly && !value.matchData?.exact) { + return; + } + //ファイルパスが.mdで終わらなければ(マークダウンファイルでなければ)スキップ + if (this.settings.markdownOnly && !value.filePath.endsWith(".md")){ + return; + } + // ファイルパスにPNのフォルダパスが含まれていない && PNのフォルダパスが指定されている のときは処理をスキップ + // *現状のPNベータでは、カレンダーセットの指定にかかわらず全セットに含まれるPNが返されるようであるため、各セットのフォルダパスでフィルタリングする + // ただしフォルダパスが指定されていないときはスキップ + if (!value.filePath.startsWith(this.calendarSets[this.activeSet][GRANULARITY_LIST[this.activeGranularity]]?.folder.concat("/")) && this.calendarSets[this.activeSet][GRANULARITY_LIST[this.activeGranularity]]?.folder.folder){ + return; + } + if (value.granularity == GRANULARITY_LIST[this.activeGranularity] && value.date.isBefore(targetDate)){ + targetDate = value.date + } + }) + return targetDate; + } +} + + +// 実体のないデイリーノートへのリンクを検出してノートを作成 +export async function checkUnresolvedLinks(){ + if (this.verPN === void 0){ + return; + } + const unresolved = this.app.metadataCache.unresolvedLinks; + const allunresolvedDNLinks: string[] = []; + let createdDN = 0; + let notice: Notice; + + for (const fileName in unresolved){ + for (const unresolvedLink in unresolved[fileName]){ + if (!notice && createdDN>=5){ + notice = new Notice(`Daily Note Outline: created ${createdDN} daily notes for unresolved links`,0) + } + allunresolvedDNLinks.push(unresolvedLink); + + if(this.verPN != 2){ + // デイリーノートまたはPeriodic Notes v0.x + const linkedDate = getDateFromPath(unresolvedLink,'day'); + if (!linkedDate){ + continue; + } + // ノートを作成 + await createDailyNoteForUnresolvedLink(this.app, linkedDate); + createdDN++; + if (notice){ + notice.setMessage(`Daily Note Outline: created ${createdDN} daily notes for unresolved links`); + } + } else { + // Periodic Notes v1.x + for (const calendarSet of this.calendarSets){ + if (!calendarSet.day.enabled){ + continue; + } + const format = calendarSet.day.format; + const linkedDate = window.moment(unresolvedLink, format, true) + if (linkedDate.isValid()){ + await createDailyNoteForUnresolvedLinkPNbeta(this.app, linkedDate, calendarSet); + createdDN++; + } + } + } + } + } + if (notice) { + new Notice('Daily Note Outline: created all daily notes for unresolved links'); + notice.hide(); + } +} + + diff --git a/src/view.ts b/src/view.ts index 4ee98b5..4bb4866 100644 --- a/src/view.ts +++ b/src/view.ts @@ -6,14 +6,10 @@ import { getAllDailyNotes, getDateFromFile, createDailyNote, getDateUID, getAllWeeklyNotes, getAllMonthlyNotes, getAllQuarterlyNotes, getAllYearlyNotes, IGranularity } from "obsidian-daily-notes-interface"; import moment from "moment" -import DailyNoteOutlinePlugin, { DailyNoteOutlineSettings, OutlineData, FileInfo, FileStatus, DAYS_PER_UNIT,GRANULARITY_LIST, GRANULARITY_TO_PERIODICITY, FILEINFO_TO_DISPLAY, FILEINFO_TO_DISPLAY_DAY, DateInfo} from 'src/main'; - -import { createAndOpenDailyNote } from 'src/createAndOpenDailyNote'; -import { ModalExtract } from 'src/modalExtract'; +import DailyNoteOutlinePlugin, { DailyNoteOutlineSettings, OutlineData, FileInfo, DAYS_PER_UNIT,GRANULARITY_LIST, GRANULARITY_TO_PERIODICITY, FILEINFO_TO_DISPLAY, FILEINFO_TO_DISPLAY_DAY, DateInfo} from 'src/main'; // Periodic Notes import {type CalendarSet,} from 'src/periodicNotesTypes'; -import { createAndOpenPeriodicNote } from './createAndOpenPeriodicNote'; import { drawUI } from 'src/drawUI'; import { getTargetFiles, getTargetPeriodicNotes } from './getTargetFiles'; @@ -21,6 +17,7 @@ import { getFileInfo, getOutline } from './getOutline'; import { drawOutline } from './constructDOM'; import { getAPI } from "obsidian-dataview"; +import { checkUnresolvedLinks } from './util'; @@ -58,7 +55,7 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS flagRegetAll: boolean; extractMode: boolean = false; - extractTask: boolean = false; + extractType: 'noraml'|'task'|'listCallout'|'time' = 'noraml'; //全ファイルの折りたたみ collapseAll: boolean = false; @@ -68,7 +65,7 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS calendarSets: CalendarSet[]; activeSet: number = 0; activeGranularity: number = 0; // day | week | month | quarter | year GRANULARITY_LISTの添え字として - + constructor( leaf: WorkspaceLeaf, plugin: DailyNoteOutlinePlugin, @@ -78,20 +75,19 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS this.plugin = plugin; this.settings = settings; } - + getViewType(): string { return DailyNoteOutlineViewType; } - + getDisplayText(): string { return 'Daily Note Outline'; } - + getIcon(): string { return 'calendar-clock'; } - async onOpen(){ this.initView(); @@ -99,23 +95,23 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS // そうしないと起動時に全デイリーノートのデータ取得に失敗するようだったため。 //自動更新のためのデータ変更、ファイル追加/削除の監視 observe file change/create/delete - const debouncerRequestRefresh:Debouncer<[]> = debounce(this.autoRefresh,2000,true); + const debouncedAutoRefresh:Debouncer<[],null> = debounce(this.autoRefresh,2000,true); this.flagRedraw = false; this.flagRegetAll = false; - this.registerEvent(this.app.metadataCache.on('changed', (file) => { + this.registerEvent(this.app.metadataCache.on('changed', (file,data,cache) => { if (this.targetFiles?.includes(file)){ this.flagRedraw = true; - debouncerRequestRefresh.call(this); + debouncedAutoRefresh.call(this); } })); this.registerEvent(this.app.vault.on('create',(file)=>{ this.flagRegetAll = true; - debouncerRequestRefresh.call(this); + debouncedAutoRefresh.call(this); })); this.registerEvent(this.app.vault.on('delete',(file)=>{ this.flagRegetAll = true; - debouncerRequestRefresh.call(this); + debouncedAutoRefresh.call(this); })); } @@ -146,6 +142,9 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS this.collapseAll = this.settings.collapseAllAtStartup; this.verPN = await this.checkPeriodicNotes(); + if (this.settings.checkUnresolvedLinksAtStartup){ + await checkUnresolvedLinks.call(this); + } this.resetSearchRange(); this.refreshView(true, true, true); @@ -153,7 +152,7 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS } private async bootDelay(): Promise { - return new Promise(resolve => { setTimeout(resolve, 300);}); + return new Promise(resolve => { setTimeout(resolve, this.settings.bootDelayTime);}); } private async autoRefresh(){ @@ -184,8 +183,6 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS this.allDailyNotes = this.getAllNotes(); } - // await this.testBacklinks(); - if (flagGetTarget){ this.targetFiles = getTargetFiles.call(this, this.allDailyNotes, GRANULARITY_LIST[this.activeGranularity]); } @@ -235,28 +232,26 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS // 探索範囲を設定に合わせて初期化 public resetSearchRange():void { - if (this.settings.initialSearchType == 'forward'){ + if (this.settings.setBaseDateAsHome == true){ const onsetDate = window.moment(this.settings.onset,"YYYY-MM-DD"); if (onsetDate.isValid()){ - this.searchRange.earliest = onsetDate; - this.searchRange.latest = onsetDate.clone().add(this.settings.duration.day -1,'days'); - } else { + this.searchRange.latest = onsetDate; + this.searchRange.earliest = onsetDate.clone().subtract(this.settings.duration.day -1,'days'); + } else { // onsetDateが不正なら当日起点のbackward searchを行う new Notice('onset date is invalid'); this.searchRange.latest = window.moment().startOf('day'); this.searchRange.earliest = window.moment().startOf('day').subtract(this.settings.duration.day - 1,'days') } - } - - if (this.settings.initialSearchType == 'backward'){ + } else { this.searchRange.latest = window.moment().startOf('day'); this.searchRange.earliest = window.moment().startOf('day').subtract(this.settings.duration.day - 1 ,'days'); - } + } } // Periodic Notesの状態をチェック return 0:オフ 1:v0.x.x 2:v1.0.0- private async checkPeriodicNotes(): Promise { - + if (this.settings.showDebugInfo){ console.log('DNO:checking the version of Periodic Notes plugin'); } @@ -267,10 +262,10 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS this.calendarSets = this.app.plugins.getPlugin("periodic-notes")?.calendarSetManager.getCalendarSets(); if (this.settings.showDebugInfo){ - console.log('return of this.app.PN.calendarSetManager.getCalendarSets(): ',this.app.plugins.getPlugin("periodic-notes")?.calendarSetManager.getCalendarSets()); - console.log('return of window.app.PN.calendarSetManager.getCalendarSets(): ',window.app.plugins.getPlugin("periodic-notes")?.calendarSetManager.getCalendarSets()); + console.log('DNO: return of this.app.PN.calendarSetManager.getCalendarSets(): ',this.app.plugins.getPlugin("periodic-notes")?.calendarSetManager.getCalendarSets()); + console.log('DNO: return of window.app.PN.calendarSetManager.getCalendarSets(): ',window.app.plugins.getPlugin("periodic-notes")?.calendarSetManager.getCalendarSets()); } - + if (!this.calendarSets.length){ new Notice('failed to import calendar sets to Daily Note Outline'); } @@ -284,7 +279,7 @@ export class DailyNoteOutlineView extends ItemView implements IDailyNoteOutlineS this.app.workspace.requestSaveLayout(); if (this.settings.showDebugInfo){ - console.log('obtained verPN: ', this.verPN ,' calendarSets: ', this.calendarSets); + console.log('DNO: obtained verPN: ', this.verPN ,' calendarSets: ', this.calendarSets); } return 2; } else { diff --git a/styles.css b/styles.css index 106307c..bd2db68 100644 --- a/styles.css +++ b/styles.css @@ -60,6 +60,12 @@ display: inline-block; flex-shrink: 1; + align-items: normal; +} + +/* wrap-lineクラスがついていたらwhite-space設定をnormalに */ +.workspace-leaf-content[data-type="daily-note-outline"] .nav-file-title-content.wrap-line { + white-space: normal; } /* icon */ @@ -90,8 +96,9 @@ /*インラインプレビュー inline preview */ /*アウトライン要素行の各項目を中央揃えで同じ高さに表示 align center*/ .workspace-leaf-content[data-type="daily-note-outline"] .nav-file-title{ - align-items: center; + align-items: normal; } + /*インラインプレビューのフォント inline preview font*/ .workspace-leaf-content[data-type="daily-note-outline"] .nav-file-title-preview{ display: inline-block; @@ -102,7 +109,8 @@ padding-left: 16px; /*margin-left:auto; 右寄せ*/ color: var(--color-base-50); - flex-shrink: 1000; + flex-shrink: 10000; + align-self: center; } /*ツールチッププレビュー*/