mirror of
https://github.com/benstuart0/on-this-day-i-obsidian.git
synced 2026-07-22 05:38:19 +00:00
Working version of on this day ai
This commit is contained in:
parent
8118bf7aaf
commit
da25e98f17
9 changed files with 3038 additions and 112 deletions
352
main.ts
352
main.ts
|
|
@ -1,85 +1,39 @@
|
||||||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
import { MarkdownView, Notice, Plugin, TFolder } from 'obsidian';
|
||||||
|
import OpenAI from 'openai';
|
||||||
|
import { OnThisDayPluginSettings, DEFAULT_SETTINGS } from 'src/OnThisDayPluginSettings';
|
||||||
|
import OnThisDaySettingTab from 'src/OnThisDaySettingTab';
|
||||||
|
import aiPrompt from 'src/prompt';
|
||||||
|
|
||||||
// Remember to rename these classes and interfaces!
|
//
|
||||||
|
// Main Plugin Class
|
||||||
interface MyPluginSettings {
|
//
|
||||||
mySetting: string;
|
export default class OnThisDayPlugin extends Plugin {
|
||||||
}
|
settings: OnThisDayPluginSettings;
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
|
||||||
mySetting: 'default'
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MyPlugin extends Plugin {
|
|
||||||
settings: MyPluginSettings;
|
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
|
console.log('Loading On This Day AI plugin');
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
this.addSettingTab(new OnThisDaySettingTab(this.app, this));
|
||||||
|
|
||||||
// This creates an icon in the left ribbon.
|
|
||||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
|
||||||
// Called when the user clicks the icon.
|
|
||||||
new Notice('This is a notice!');
|
|
||||||
});
|
|
||||||
// Perform additional things with the ribbon
|
|
||||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
|
||||||
|
|
||||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
|
||||||
const statusBarItemEl = this.addStatusBarItem();
|
|
||||||
statusBarItemEl.setText('Status Bar Text');
|
|
||||||
|
|
||||||
// This adds a simple command that can be triggered anywhere
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'open-sample-modal-simple',
|
id: 'on-this-day-ai:on-this-day-placeholder',
|
||||||
name: 'Open sample modal (simple)',
|
name: 'Add On This Day AI Placeholder',
|
||||||
callback: () => {
|
callback: async () => {
|
||||||
new SampleModal(this.app).open();
|
await this.addPlaceholder();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
// This adds an editor command that can perform some operation on the current editor instance
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'sample-editor-command',
|
id: 'on-this-day-ai:generate-date-summaries',
|
||||||
name: 'Sample editor command',
|
name: 'Generate Through The Years',
|
||||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
callback: async () => {
|
||||||
console.log(editor.getSelection());
|
await this.generateDateSummaries();
|
||||||
editor.replaceSelection('Sample Editor Command');
|
},
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
|
||||||
this.addCommand({
|
|
||||||
id: 'open-sample-modal-complex',
|
|
||||||
name: 'Open sample modal (complex)',
|
|
||||||
checkCallback: (checking: boolean) => {
|
|
||||||
// Conditions to check
|
|
||||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
|
||||||
if (markdownView) {
|
|
||||||
// If checking is true, we're simply "checking" if the command can be run.
|
|
||||||
// If checking is false, then we want to actually perform the operation.
|
|
||||||
if (!checking) {
|
|
||||||
new SampleModal(this.app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This command will only show up in Command Palette when the check function returns true
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
|
||||||
this.addSettingTab(new SampleSettingTab(this.app, this));
|
|
||||||
|
|
||||||
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
|
|
||||||
// Using this function will automatically remove the event listener when this plugin is disabled.
|
|
||||||
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
|
|
||||||
console.log('click', evt);
|
|
||||||
});
|
|
||||||
|
|
||||||
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
|
|
||||||
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
async onunload() {
|
||||||
|
console.log('Unloading On This Day AI plugin');
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
|
|
@ -89,46 +43,236 @@ export default class MyPlugin extends Plugin {
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
await this.saveData(this.settings);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class SampleModal extends Modal {
|
//
|
||||||
constructor(app: App) {
|
// Utility Functions
|
||||||
super(app);
|
//
|
||||||
|
|
||||||
|
// Check if a string matches the expected date format ("MMMM D, YYYY")
|
||||||
|
isValidDateString(dateString: string): boolean {
|
||||||
|
const regex =
|
||||||
|
/^(January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}, \d{4}$/;
|
||||||
|
return regex.test(dateString);
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpen() {
|
// Parse a date string (assumes "MMMM D, YYYY")
|
||||||
const {contentEl} = this;
|
parseDateFromString(dateString: string, format: string): Date | null {
|
||||||
contentEl.setText('Woah!');
|
// This regex is based on the expected format "MMMM D, YYYY"
|
||||||
|
const regex =
|
||||||
|
/^(January|February|March|April|May|June|July|August|September|October|November|December) (\d{1,2}), (\d{4})$/;
|
||||||
|
const match = dateString.match(regex);
|
||||||
|
if (match) {
|
||||||
|
const monthName = match[1];
|
||||||
|
const day = parseInt(match[2]);
|
||||||
|
const year = parseInt(match[3]);
|
||||||
|
const date = new Date(`${monthName} ${day}, ${year}`);
|
||||||
|
if (!isNaN(date.getTime())) {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
//
|
||||||
const {contentEl} = this;
|
// Main Function: Generate Date Summaries
|
||||||
contentEl.empty();
|
//
|
||||||
}
|
async generateDateSummaries() {
|
||||||
}
|
// 1. Ensure the command is run from a file with a valid date as its title.
|
||||||
|
const activeFile = this.app.workspace.getActiveFile();
|
||||||
|
if (!activeFile) {
|
||||||
|
new Notice('No active file found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.isValidDateString(activeFile.basename)) {
|
||||||
|
new Notice(`This command must be run from a file with a date title in the format ${this.settings.dateFormat}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
class SampleSettingTab extends PluginSettingTab {
|
const inputDateString = activeFile.basename;
|
||||||
plugin: MyPlugin;
|
|
||||||
|
|
||||||
constructor(app: App, plugin: MyPlugin) {
|
// 2. Parse the input date to extract month and day (ignoring the year)
|
||||||
super(app, plugin);
|
const parsedDate = this.parseDateFromString(inputDateString, this.settings.dateFormat);
|
||||||
this.plugin = plugin;
|
if (!parsedDate) {
|
||||||
|
new Notice('Could not parse the date from the file title.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const month = parsedDate.getMonth(); // 0-based month
|
||||||
|
const day = parsedDate.getDate();
|
||||||
|
const currentYear = parsedDate.getFullYear();
|
||||||
|
|
||||||
|
// 3. Search through the daily notes folder for matching files
|
||||||
|
const folder = this.app.vault.getAbstractFileByPath(this.settings.dailyNotesFolder);
|
||||||
|
if (!folder || !(folder instanceof TFolder)) {
|
||||||
|
new Notice('Daily notes folder not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all Markdown files in the daily notes folder
|
||||||
|
const files = this.app.vault
|
||||||
|
.getFiles()
|
||||||
|
.filter(
|
||||||
|
(file) =>
|
||||||
|
file.path.startsWith(this.settings.dailyNotesFolder) && file.extension === 'md'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build a map: year => concatenated content of matching daily notes
|
||||||
|
let yearToContent: Record<string, string> = {};
|
||||||
|
for (const file of files) {
|
||||||
|
const fileDate = this.parseDateFromString(file.basename, this.settings.dateFormat);
|
||||||
|
if (!fileDate) continue;
|
||||||
|
if (fileDate.getMonth() === month && fileDate.getDate() === day && fileDate.getFullYear() != currentYear) {
|
||||||
|
const yearStr = fileDate.getFullYear().toString();
|
||||||
|
const content = await this.app.vault.read(file);
|
||||||
|
if (yearToContent[yearStr]) {
|
||||||
|
yearToContent[yearStr] += '\n' + content;
|
||||||
|
} else {
|
||||||
|
yearToContent[yearStr] = content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(yearToContent).length === 0) {
|
||||||
|
new Notice('No matching daily notes found for that date.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Construct the API prompt that instructs GPT-4 to produce a JSON summary mapping.
|
||||||
|
const prompt = this.constructPrompt(yearToContent, inputDateString);
|
||||||
|
|
||||||
|
// 5. Show a loading indicator (persistent notice) until work is done.
|
||||||
|
const loadingNotice = new Notice("Generating summaries...", 0);
|
||||||
|
|
||||||
|
// 6. Call the OpenAI API once with the entire JSON map.
|
||||||
|
let responseJSON: Record<string, string>;
|
||||||
|
try {
|
||||||
|
responseJSON = await this.callOpenAI(prompt);
|
||||||
|
} catch (error: any) {
|
||||||
|
loadingNotice.hide();
|
||||||
|
console.error('API call failed: ' + error.message);
|
||||||
|
new Notice('API call failed: ' + error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Build the output markdown block.
|
||||||
|
const outputBlock = this.buildOutputBlock(responseJSON, this.settings.horizontalRules);
|
||||||
|
|
||||||
|
// 8. Insert the output block by looking for a placeholder marker.
|
||||||
|
// Define a unique placeholder marker.
|
||||||
|
const placeholder = "<!OTD>";
|
||||||
|
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||||
|
if (!activeView) {
|
||||||
|
loadingNotice.hide();
|
||||||
|
new Notice('No active editor found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = (activeView as MarkdownView).editor;
|
||||||
|
const currentContent = editor.getValue();
|
||||||
|
let newContent = "";
|
||||||
|
|
||||||
|
if (currentContent.includes(placeholder)) {
|
||||||
|
// Replace the placeholder with the output block.
|
||||||
|
newContent = currentContent.replace(placeholder, outputBlock);
|
||||||
|
editor.setValue(newContent);
|
||||||
|
} else {
|
||||||
|
// If no placeholder is found, place block at cursor location
|
||||||
|
editor.replaceSelection(outputBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.setValue(newContent);
|
||||||
|
|
||||||
|
// 9. Hide the loading notice and show a completion notice.
|
||||||
|
loadingNotice.hide();
|
||||||
|
new Notice('On This Day summaries inserted.');
|
||||||
}
|
}
|
||||||
|
|
||||||
display(): void {
|
//
|
||||||
const {containerEl} = this;
|
// Construct the API Prompt
|
||||||
|
//
|
||||||
containerEl.empty();
|
constructPrompt(yearToContent: Record<string, string>, inputDateString: string): string {
|
||||||
|
const promptInstructions = aiPrompt
|
||||||
new Setting(containerEl)
|
const dataString = JSON.stringify(yearToContent);
|
||||||
.setName('Setting #1')
|
return `${promptInstructions}\n\nData:\n${dataString}`;
|
||||||
.setDesc('It\'s a secret')
|
|
||||||
.addText(text => text
|
|
||||||
.setPlaceholder('Enter your secret')
|
|
||||||
.setValue(this.plugin.settings.mySetting)
|
|
||||||
.onChange(async (value) => {
|
|
||||||
this.plugin.settings.mySetting = value;
|
|
||||||
await this.plugin.saveSettings();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//
|
||||||
|
// Call the OpenAI API
|
||||||
|
//
|
||||||
|
async callOpenAI(prompt: string): Promise<Record<string, string>> {
|
||||||
|
const client = new OpenAI({
|
||||||
|
apiKey: this.settings.openaiApiKey,
|
||||||
|
dangerouslyAllowBrowser: true
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.chat.completions.create({
|
||||||
|
model: this.settings.model,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'system',
|
||||||
|
content:
|
||||||
|
'You are a helpful assistant that summarizes daily journal entries in a strict JSON format.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: prompt,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
temperature: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate the response structure
|
||||||
|
if (
|
||||||
|
!response.choices ||
|
||||||
|
response.choices.length === 0 ||
|
||||||
|
!response.choices[0].message?.content
|
||||||
|
) {
|
||||||
|
throw new Error('No valid response received from OpenAI.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = response.choices[0].message.content;
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(content);
|
||||||
|
return result;
|
||||||
|
} catch (parseError: any) {
|
||||||
|
console.error('Failed to parse JSON from OpenAI response:', parseError);
|
||||||
|
throw new Error(
|
||||||
|
'Failed to parse JSON from OpenAI response: ' + parseError.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('OpenAI API error:', error);
|
||||||
|
throw new Error('OpenAI API error: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Build the Output Markdown
|
||||||
|
//
|
||||||
|
buildOutputBlock(summaries: Record<string, string>, horizontalRules: string): string {
|
||||||
|
// Add horizontal rules based on settings
|
||||||
|
let output = '## On This Day...\n';
|
||||||
|
if (horizontalRules.includes('Above')) {
|
||||||
|
output = '---\n## On This Day...\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, sort the years in descending order
|
||||||
|
const years = Object.keys(summaries).sort((a, b) => parseInt(b) - parseInt(a));
|
||||||
|
for (const year of years) {
|
||||||
|
output += `- **${year}:** ${summaries[year]}\n`;
|
||||||
|
}
|
||||||
|
if (horizontalRules.includes('Below')) {
|
||||||
|
output += "\n---"
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPlaceholder() {
|
||||||
|
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||||
|
if (!activeView) {
|
||||||
|
new Notice('No active editor found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const editor = (activeView as MarkdownView).editor;
|
||||||
|
editor.replaceSelection('<!OTD>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"id": "sample-plugin",
|
"id": "on-this-day-ai-obsidian-plugin",
|
||||||
"name": "Sample Plugin",
|
"name": "On This Day AI",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"minAppVersion": "0.15.0",
|
"minAppVersion": "0.15.0",
|
||||||
"description": "Demonstrates some of the capabilities of the Obsidian API.",
|
"description": "Use AI to remind yourself what has happened on this date in past years.",
|
||||||
"author": "Obsidian",
|
"author": "Ben Stuart",
|
||||||
"authorUrl": "https://obsidian.md",
|
"authorUrl": "https://github.com/benstuart0/on-this-day-ai-obsidian-plugin",
|
||||||
"fundingUrl": "https://obsidian.md/pricing",
|
"fundingUrl": "https://obsidian.md/pricing",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2646
package-lock.json
generated
Normal file
2646
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "obsidian-sample-plugin",
|
"name": "on-this-day-ai-obsidian-plugin",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
"description": "This is the first version of the On This Day AI Obsidian plugin",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "Ben Stuart",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^16.11.6",
|
"@types/node": "^16.11.6",
|
||||||
|
|
@ -20,5 +20,8 @@
|
||||||
"obsidian": "latest",
|
"obsidian": "latest",
|
||||||
"tslib": "2.4.0",
|
"tslib": "2.4.0",
|
||||||
"typescript": "4.7.4"
|
"typescript": "4.7.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"openai": "^4.83.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/OnThisDayPluginSettings.ts
Normal file
18
src/OnThisDayPluginSettings.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// Settings Interfaces & Defaults
|
||||||
|
//
|
||||||
|
export interface OnThisDayPluginSettings {
|
||||||
|
openaiApiKey: string;
|
||||||
|
model: string;
|
||||||
|
dailyNotesFolder: string;
|
||||||
|
dateFormat: string;
|
||||||
|
horizontalRules: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_SETTINGS: OnThisDayPluginSettings = {
|
||||||
|
openaiApiKey: '',
|
||||||
|
model: 'gpt-4',
|
||||||
|
dailyNotesFolder: '010 Daily Notes',
|
||||||
|
dateFormat: 'MMMM D, YYYY',
|
||||||
|
horizontalRules: ''
|
||||||
|
};
|
||||||
106
src/OnThisDaySettingTab.ts
Normal file
106
src/OnThisDaySettingTab.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { App, PluginSettingTab, Setting, TFolder } from 'obsidian';
|
||||||
|
import moment from 'moment';
|
||||||
|
import OnThisDayPlugin from 'main';
|
||||||
|
import { validateInputTools } from 'openai/lib/parser';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Settings Tab
|
||||||
|
//
|
||||||
|
export default class OnThisDaySettingTab extends PluginSettingTab {
|
||||||
|
plugin: OnThisDayPlugin;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: OnThisDayPlugin) {
|
||||||
|
super(app, plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
display(): void {
|
||||||
|
const { containerEl } = this;
|
||||||
|
containerEl.empty();
|
||||||
|
containerEl.createEl('h2', { text: 'On This Day AI Settings' });
|
||||||
|
|
||||||
|
// Date Format Setting with dynamic example using the current setting
|
||||||
|
const dateSetting = new Setting(containerEl)
|
||||||
|
.setName('Date Format')
|
||||||
|
.setDesc(`Format for daily note filenames. Example: ${moment(new Date()).format(this.plugin.settings.dateFormat)}`)
|
||||||
|
.addText((text) => {
|
||||||
|
text
|
||||||
|
.setPlaceholder('MMMM D, YYYY')
|
||||||
|
.setValue(this.plugin.settings.dateFormat)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings.dateFormat = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
// Update the description with a new example using the updated format
|
||||||
|
const newExample = moment(new Date()).format(value);
|
||||||
|
dateSetting.setDesc(`Format for daily note filenames. Example: ${newExample}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Daily Notes Folder Setting
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Daily Notes Folder')
|
||||||
|
.setDesc('Select the folder where your daily notes are stored.')
|
||||||
|
.addDropdown((dropdown) => {
|
||||||
|
// Create an object to hold the folder options
|
||||||
|
const folderOptions: Record<string, string> = {};
|
||||||
|
// Get all files from the vault and filter for folders
|
||||||
|
const allFolders = this.app.vault.getAllLoadedFiles().filter((f) => f instanceof TFolder) as TFolder[];
|
||||||
|
// Populate the folderOptions with folder paths
|
||||||
|
allFolders.forEach((folder) => {
|
||||||
|
folderOptions[folder.path] = folder.path;
|
||||||
|
});
|
||||||
|
// Add each folder option to the dropdown
|
||||||
|
for (const key in folderOptions) {
|
||||||
|
dropdown.addOption(key, folderOptions[key]);
|
||||||
|
}
|
||||||
|
// Set the current value from the plugin settings
|
||||||
|
dropdown.setValue(this.plugin.settings.dailyNotesFolder);
|
||||||
|
dropdown.onChange(async (value: string) => {
|
||||||
|
this.plugin.settings.dailyNotesFolder = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// OpenAI API Key Setting
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('OpenAI API Key')
|
||||||
|
.setDesc('Your OpenAI API key.')
|
||||||
|
.addText((text) =>
|
||||||
|
text
|
||||||
|
.setPlaceholder('sk-...')
|
||||||
|
.setValue(this.plugin.settings.openaiApiKey)
|
||||||
|
.onChange(async (value) => {
|
||||||
|
this.plugin.settings.openaiApiKey = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Model Version Dropdown Setting
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Model Version')
|
||||||
|
.setDesc('Select the OpenAI model to use.')
|
||||||
|
.addDropdown((dropdown) => {
|
||||||
|
dropdown.addOption('gpt-3.5-turbo', 'gpt-3.5-turbo');
|
||||||
|
dropdown.addOption('gpt-4', 'gpt-4');
|
||||||
|
dropdown.setValue(this.plugin.settings.model);
|
||||||
|
dropdown.onChange(async (value: string) => {
|
||||||
|
this.plugin.settings.model = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName('Horizontal Rules')
|
||||||
|
.setDesc('Would you like horizontal rules above or below the output block?')
|
||||||
|
.addDropdown((dropdown) => {
|
||||||
|
dropdown.addOption('', 'None');
|
||||||
|
dropdown.addOption('Below', 'Below');
|
||||||
|
dropdown.addOption('Above', 'Above');
|
||||||
|
dropdown.addOption('Above Below', 'Both');
|
||||||
|
dropdown.setValue(this.plugin.settings.horizontalRules);
|
||||||
|
dropdown.onChange(async (value: string) => {
|
||||||
|
this.plugin.settings.horizontalRules = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/aiPrompt.txt
Normal file
5
src/aiPrompt.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const aiPrompt = "Below is a JSON object where each key is a year and each value is the full content of my daily journal for this day on that given year.
|
||||||
|
|
||||||
|
For each year, please provide an interesting summary in 2-3 sentences that highlights the events of that day. Try to include who I spent time with (including their names), what I did, and how I felt.
|
||||||
|
|
||||||
|
Use second-person language (you), as you are referring to me - the writer of the journals. Do not include the date in the summary. Return only a JSON object mapping each year to its summary with no extra text, headers, or footers."
|
||||||
3
src/prompt.ts
Normal file
3
src/prompt.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
const aiPrompt = "Below is a JSON object where each key is a year and each value is the full content of my daily journal for this day on that given year. For each year, please provide an interesting summary in 2-3 sentences that first highlights the most interesting events of the day and then describes how I felt, using second-person language (you), as you are referring to me - the writer of the journals. Do not include the date in the summary. Return only a JSON object mapping each year to its summary with no extra text, headers, or footers."
|
||||||
|
|
||||||
|
export default aiPrompt
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM",
|
"DOM",
|
||||||
"ES5",
|
"ES5",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue