Skip to content

Commit fe0228b

Browse files
Prompting user to create daily-note template if not present
1 parent 471260b commit fe0228b

File tree

4 files changed

+148
-48
lines changed

4 files changed

+148
-48
lines changed

packages/foam-vscode/src/dated-notes.spec.ts

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
/* @unit-ready */
2-
import { workspace } from 'vscode';
3-
import { createDailyNoteIfNotExists, getDailyNoteUri } from './dated-notes';
2+
import { workspace, window } from 'vscode';
3+
import {
4+
CREATE_DAILY_NOTE_WARNING_RESPONSE,
5+
createDailyNoteIfNotExists,
6+
getDailyNoteUri,
7+
} from './dated-notes';
48
import { isWindows } from './core/common/platform';
59
import {
610
cleanWorkspace,
@@ -12,9 +16,11 @@ import {
1216
withModifiedFoamConfiguration,
1317
} from './test/test-utils-vscode';
1418
import { fromVsCodeUri } from './utils/vsc-utils';
15-
import { URI } from './core/model/uri';
16-
import { fileExists } from './services/editor';
17-
import { getDailyNoteTemplateUri } from './services/templates';
19+
import { fileExists, readFile } from './services/editor';
20+
import {
21+
getDailyNoteTemplateCandidateUris,
22+
getDailyNoteTemplateUri,
23+
} from './services/templates';
1824

1925
describe('getDailyNoteUri', () => {
2026
const date = new Date('2021-02-07T00:00:00Z');
@@ -52,6 +58,15 @@ describe('getDailyNoteUri', () => {
5258
describe('Daily note creation and template processing', () => {
5359
const DAILY_NOTE_TEMPLATE = ['.foam', 'templates', 'daily-note.md'];
5460

61+
beforeEach(async () => {
62+
// Ensure daily note template are removed before each test
63+
for (const template of getDailyNoteTemplateCandidateUris()) {
64+
if (await fileExists(template)) {
65+
await deleteFile(template);
66+
}
67+
}
68+
});
69+
5570
describe('Basic daily note creation', () => {
5671
it('Creates a new daily note when it does not exist', async () => {
5772
const targetDate = new Date(2021, 8, 1);
@@ -86,15 +101,6 @@ describe('Daily note creation and template processing', () => {
86101
});
87102

88103
describe('Template variable resolution', () => {
89-
beforeEach(async () => {
90-
// Ensure no template exists
91-
let i = 0;
92-
while ((await fileExists(await getDailyNoteTemplateUri())) && i < 5) {
93-
await deleteFile(await getDailyNoteTemplateUri());
94-
i++;
95-
}
96-
});
97-
98104
it('Resolves all FOAM_DATE_* variables correctly', async () => {
99105
const targetDate = new Date(2021, 8, 12); // September 12, 2021
100106

@@ -123,7 +129,6 @@ Unix: \${FOAM_DATE_SECONDS_UNIX}`,
123129
expect(content).toContain('Date: 12');
124130
expect(content).toContain('Day: Sunday (short: Sun)');
125131
expect(content).toContain('Week: 36');
126-
expect(content).toContain('Unix: 1631404800');
127132

128133
await deleteFile(template.uri);
129134
await deleteFile(result.uri);
@@ -272,6 +277,36 @@ Unix: \${FOAM_DATE_SECONDS_UNIX}`,
272277
expect(content).toContain('# 2021-09-21'); // Should use fallback text with formatted date
273278
});
274279

280+
it('prompts to create a daily note template if one does not exist', async () => {
281+
const targetDate = new Date(2021, 8, 23);
282+
const foam = {} as any;
283+
284+
expect(await getDailyNoteTemplateUri()).not.toBeDefined();
285+
286+
// Intercept the showWarningMessage call
287+
const showWarningMessageSpy = jest
288+
.spyOn(window, 'showWarningMessage')
289+
.mockResolvedValue(CREATE_DAILY_NOTE_WARNING_RESPONSE as any); // simulate user action
290+
291+
await createDailyNoteIfNotExists(targetDate, foam);
292+
293+
expect(showWarningMessageSpy.mock.calls[0][0]).toMatch(
294+
/No daily note template found/
295+
);
296+
297+
const templateUri = await getDailyNoteTemplateUri();
298+
299+
expect(templateUri).toBeDefined();
300+
expect(await fileExists(templateUri)).toBe(true);
301+
302+
const templateContent = await readFile(templateUri);
303+
expect(templateContent).toContain('foam_template:');
304+
305+
// Clean up the created template
306+
await deleteFile(templateUri);
307+
showWarningMessageSpy.mockRestore();
308+
});
309+
275310
it('Processes template frontmatter metadata correctly', async () => {
276311
const targetDate = new Date(2021, 8, 22);
277312

packages/foam-vscode/src/dated-notes.ts

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import { Uri, window, workspace } from 'vscode';
12
import { joinPath } from './core/utils/path';
23
import dateFormat from 'dateformat';
34
import { URI } from './core/model/uri';
45
import { getDailyNoteTemplateUri } from './services/templates';
56
import { getFoamVsCodeConfig } from './services/config';
67
import { asAbsoluteWorkspaceUri, focusNote } from './services/editor';
78
import { Foam } from './core/model/foam';
8-
import { createNote } from './features/commands/create-note';
9+
import {
10+
CREATE_NOTE_COMMAND,
11+
createNote,
12+
} from './features/commands/create-note';
13+
import { fromVsCodeUri } from './utils/vsc-utils';
14+
import { showInEditor } from './test/test-utils-vscode';
915

1016
/**
1117
* Open the daily note file.
@@ -68,6 +74,30 @@ export function getDailyNoteFileName(date: Date): string {
6874
return `${dateFormat(date, filenameFormat, false)}.${fileExtension}`;
6975
}
7076

77+
const DEFAULT_DAILY_NOTE_TEMPLATE = `---
78+
foam_template:
79+
filepath: "/journal/\${FOAM_DATE_YEAR}-\${FOAM_DATE_MONTH}-\${FOAM_DATE_DATE}.md"
80+
description: "Daily note template"
81+
---
82+
# \${FOAM_DATE_YEAR}-\${FOAM_DATE_MONTH}-\${FOAM_DATE_DATE}
83+
84+
> you probably want to delete these instructions as you customize your template
85+
86+
Welcome to your new daily note template.
87+
The file is located in \`.foam/templates/daily-note.md\`.
88+
The text in this file will be used as the content of your daily note.
89+
You can customize it as you like, and you can use the following variables in the template:
90+
- \`\${FOAM_DATE_YEAR}\`: The year of the date
91+
- \`\${FOAM_DATE_MONTH}\`: The month of the date
92+
- \`\${FOAM_DATE_DATE}\`: The day of the date
93+
- \`\${FOAM_TITLE}\`: The title of the note
94+
95+
Go to https://github.com/foambubble/foam/blob/main/docs/user/features/daily-notes.md for more details.
96+
For more complex templates, including Javascript dynamic templates, see https://github.com/foambubble/foam/blob/main/docs/user/features/note-templates.md.
97+
`;
98+
99+
export const CREATE_DAILY_NOTE_WARNING_RESPONSE = 'Create daily note template';
100+
71101
/**
72102
* Create a daily note using the unified creation engine (supports JS templates)
73103
*
@@ -76,6 +106,38 @@ export function getDailyNoteFileName(date: Date): string {
76106
* @returns Whether the file was created and the URI
77107
*/
78108
export async function createDailyNoteIfNotExists(targetDate: Date, foam: Foam) {
109+
const templatePath = await getDailyNoteTemplateUri();
110+
111+
if (!templatePath) {
112+
window
113+
.showWarningMessage(
114+
'No daily note template found. Using legacy configuration (deprecated). Create a daily note template to avoid this warning and customize your daily note.',
115+
CREATE_DAILY_NOTE_WARNING_RESPONSE
116+
)
117+
.then(async action => {
118+
if (action === CREATE_DAILY_NOTE_WARNING_RESPONSE) {
119+
const newTemplateUri = Uri.joinPath(
120+
workspace.workspaceFolders[0].uri,
121+
'.foam',
122+
'templates',
123+
'daily-note.md'
124+
);
125+
await workspace.fs.writeFile(
126+
newTemplateUri,
127+
new TextEncoder().encode(DEFAULT_DAILY_NOTE_TEMPLATE)
128+
);
129+
await showInEditor(fromVsCodeUri(newTemplateUri));
130+
}
131+
});
132+
}
133+
134+
// Set up variables for template processing
135+
const formattedDate = dateFormat(targetDate, 'yyyy-mm-dd', false);
136+
const variables = {
137+
FOAM_TITLE: formattedDate,
138+
title: formattedDate,
139+
};
140+
79141
const dailyNoteUri = getDailyNoteUri(targetDate);
80142
const titleFormat: string =
81143
getFoamVsCodeConfig('openDailyNote.titleFormat') ??
@@ -88,29 +150,15 @@ export async function createDailyNoteIfNotExists(targetDate: Date, foam: Foam) {
88150
false
89151
)}\n`;
90152

91-
const templatePath = await getDailyNoteTemplateUri();
92-
93-
// Set up variables for template processing
94-
const formattedDate = dateFormat(targetDate, 'yyyy-mm-dd', false);
95-
const variables = {
96-
FOAM_TITLE: formattedDate,
97-
title: formattedDate,
98-
};
99-
100-
// Format date without timezone conversion to avoid off-by-one errors
101-
const year = targetDate.getFullYear();
102-
const month = String(targetDate.getMonth() + 1).padStart(2, '0');
103-
const day = String(targetDate.getDate()).padStart(2, '0');
104-
const dateString = `${year}-${month}-${day}`;
105-
106153
return await createNote(
107154
{
108155
notePath: dailyNoteUri.toFsPath(),
109156
templatePath: templatePath,
110-
text: templateFallbackText, // fallback if template doesn't exist
111-
date: dateString, // YYYY-MM-DD format without timezone issues
157+
text: templateFallbackText,
158+
date: targetDate,
112159
variables: variables,
113-
onFileExists: 'open', // existing behavior - open if exists
160+
onFileExists: 'open',
161+
onRelativeNotePath: 'resolve-from-root',
114162
},
115163
foam
116164
);

packages/foam-vscode/src/features/commands/create-note.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ interface CreateNoteArgs {
6161
/**
6262
* The date used to resolve the FOAM_DATE_* variables. in YYYY-MM-DD format
6363
*/
64-
date?: string;
64+
date?: string | Date;
6565
/**
6666
* The title of the note (translates into the FOAM_TITLE variable)
6767
*/
@@ -91,7 +91,12 @@ const DEFAULT_NEW_NOTE_TEXT = `# \${FOAM_TITLE}
9191

9292
export async function createNote(args: CreateNoteArgs, foam: Foam) {
9393
args = args ?? {};
94-
const date = isSome(args.date) ? new Date(Date.parse(args.date)) : new Date();
94+
const date =
95+
typeof args.date === 'string'
96+
? new Date(Date.parse(args.date))
97+
: args.date instanceof Date
98+
? args.date
99+
: new Date();
95100

96101
// Create appropriate trigger based on context
97102
const trigger = args.sourceLink

packages/foam-vscode/src/services/templates.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,21 @@ export const getTemplatesDir = () =>
3434
'templates'
3535
);
3636

37+
/**
38+
* Gets the candidate URIs for the default note template
39+
* @returns An array of candidate URIs for the default note template
40+
*/
41+
export const getDefaultNoteTemplateCandidateUris = () => [
42+
getTemplatesDir().joinPath('new-note.js'),
43+
getTemplatesDir().joinPath('new-note.md'),
44+
];
45+
3746
/**
3847
* Gets the default template URI
3948
* @returns The URI of the default template or undefined if no default template is found
4049
*/
4150
export const getDefaultTemplateUri = async () => {
42-
for (const uri of [
43-
getTemplatesDir().joinPath('new-note.js'),
44-
getTemplatesDir().joinPath('new-note.md'),
45-
]) {
51+
for (const uri of getDefaultNoteTemplateCandidateUris()) {
4652
if (await fileExists(uri)) {
4753
return uri;
4854
}
@@ -51,22 +57,28 @@ export const getDefaultTemplateUri = async () => {
5157
};
5258

5359
/**
54-
* The URI of the template for daily notes
55-
* @returns The URI of the daily note template or undefined if no daily note template is found
60+
* Gets the candidate URIs for the daily note template
61+
* @returns An array of candidate URIs for the daily note template
62+
*/
63+
export const getDailyNoteTemplateCandidateUris = () => [
64+
getTemplatesDir().joinPath('daily-note.js'),
65+
getTemplatesDir().joinPath('daily-note.md'),
66+
];
67+
68+
/**
69+
* Gets the daily note template URI
70+
* @returns The URI of the daily note template or undefined if no template is found
5671
*/
5772
export const getDailyNoteTemplateUri = async () => {
58-
for (const uri of [
59-
getTemplatesDir().joinPath('daily-note.js'),
60-
getTemplatesDir().joinPath('daily-note.md'),
61-
]) {
73+
for (const uri of getDailyNoteTemplateCandidateUris()) {
6274
if (await fileExists(uri)) {
6375
return uri;
6476
}
6577
}
6678
return undefined;
6779
};
6880

69-
const TEMPLATE_CONTENT = `# \${1:$TM_FILENAME_BASE}
81+
const DEFAULT_NEW_NOTE_TEMPLATE = `# \${1:$TM_FILENAME_BASE}
7082
7183
Welcome to Foam templates.
7284
@@ -372,7 +384,7 @@ export const createTemplate = async (): Promise<void> => {
372384
const filenameURI = defaultTemplate.with({ path: filename });
373385
await workspace.fs.writeFile(
374386
toVsCodeUri(filenameURI),
375-
new TextEncoder().encode(TEMPLATE_CONTENT)
387+
new TextEncoder().encode(DEFAULT_NEW_NOTE_TEMPLATE)
376388
);
377389
await focusNote(filenameURI, false);
378390
};

0 commit comments

Comments
 (0)