diff --git a/src/core/formDefinition.ts b/src/core/formDefinition.ts index a2b6e193..6739d9c9 100644 --- a/src/core/formDefinition.ts +++ b/src/core/formDefinition.ts @@ -16,6 +16,7 @@ import { } from "./formDefinitionSchema"; import { A, O, pipe } from "@std"; import { ParsedTemplate } from "./template/templateParser"; +import { Simplify } from "type-fest"; //=========== Types derived from schemas type selectFromNotes = Output; type inputSlider = Output; @@ -72,7 +73,9 @@ export type FieldDefinition = Output; * FormDefinition is an already valid form, ready to be used in the form modal. */ export type FormDefinition = Output; -export type FormWithTemplate = FormDefinition & { template: ParsedTemplate } +export type FormWithTemplate = Simplify< + FormDefinition & Required> +>; export type FormOptions = { values?: Record; @@ -81,8 +84,8 @@ export type FormOptions = { type KeyOfUnion = T extends unknown ? keyof T : never; type PickUnion> = T extends unknown ? K & keyof T extends never - ? never - : Pick + ? never + : Pick : never; export type AllSources = PickUnion["source"]; diff --git a/src/main.ts b/src/main.ts index fa3311fa..c81f39b9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,45 +7,62 @@ import { EDIT_FORM_VIEW, EditFormView } from "src/views/EditFormView"; import { MANAGE_FORMS_VIEW, ManageFormsView } from "src/views/ManageFormsView"; import { ModalFormError } from "src/utils/ModalFormError"; import { FormWithTemplate, type FormDefinition } from "src/core/formDefinition"; -import { formNeedsMigration, migrateToLatest, MigrationError, InvalidData } from "./core/formDefinitionSchema"; -import { parseSettings, type ModalFormSettings, type OpenPosition, getDefaultSettings } from "src/core/settings"; +import { + formNeedsMigration, + migrateToLatest, + MigrationError, + InvalidData, +} from "./core/formDefinitionSchema"; +import { + parseSettings, + type ModalFormSettings, + type OpenPosition, + getDefaultSettings, +} from "src/core/settings"; import { log_notice } from "./utils/Log"; import * as E from "fp-ts/Either"; import { pipe } from "fp-ts/function"; -import * as A from "fp-ts/Array" +import * as A from "fp-ts/Array"; import { settingsStore } from "./store/store"; import { O } from "@std"; import { executeTemplate } from "./core/template/templateParser"; import { NewNoteModal } from "./suggesters/NewNoteModal"; import { file_exists } from "./utils/files"; +import { number } from "valibot"; type ViewType = typeof EDIT_FORM_VIEW | typeof MANAGE_FORMS_VIEW; // Define functions and properties you want to make available to other plugins, or templater templates, etc interface PublicAPI { exampleForm(): Promise; - openForm(formReference: string | FormDefinition): Promise + openForm(formReference: string | FormDefinition): Promise; } function notifyParsingErrors(errors: InvalidData[]) { - if (errors.length === 0) { return } - log_notice('Some forms could not be parsed', + if (errors.length === 0) { + return; + } + log_notice( + "Some forms could not be parsed", `We found some invalid data while parsing the form settings, please take a look at the following errors: - ${errors.join('\n')}` - ) + ${errors.join("\n")}`, + ); } function notifyMigrationErrors(errors: MigrationError[]) { - if (errors.length === 0) { return } - log_notice('Some forms could not be migrated', + if (errors.length === 0) { + return; + } + log_notice( + "Some forms could not be migrated", `We tried to perform an automatic migration, but we failed. Go to the forms manager and fix the following forms: - ${errors.map((e) => e.name).join('\n')}` - ) + ${errors.map((e) => e.name).join("\n")}`, + ); } // This is the plugin entrypoint export default class ModalFormPlugin extends Plugin { public settings: ModalFormSettings | undefined; - private unsubscribeSettingsStore: () => void = () => { }; + private unsubscribeSettingsStore: () => void = () => {}; // This things will be setup in the onload function rather than constructor public api!: PublicAPI; @@ -59,7 +76,7 @@ export default class ModalFormPlugin extends Plugin { /** * Opens the form in the editor. - * @returns + * @returns */ async editForm(formName: string) { // By reading settings from the disk we get a copy of the form @@ -68,16 +85,17 @@ export default class ModalFormPlugin extends Plugin { // then if you save another form you will unexpectedly save the mutated form too. // Maybe we could instead do a deep copy instead, but until this proven to be a bottleneck I will leave it like this. const savedSettings = await this.getSettings(); - const formDefinition = savedSettings.formDefinitions.find((form) => form.name === formName); + const formDefinition = savedSettings.formDefinitions.find( + (form) => form.name === formName, + ); if (!formDefinition) { - throw new ModalFormError(`Form ${formName} not found`) + throw new ModalFormError(`Form ${formName} not found`); } if (formDefinition instanceof MigrationError) { - notifyMigrationErrors([formDefinition]) - return + notifyMigrationErrors([formDefinition]); + return; } await this.activateView(EDIT_FORM_VIEW, formDefinition); - } closeEditForm() { @@ -90,19 +108,23 @@ export default class ModalFormPlugin extends Plugin { async activateView(viewType: ViewType, state?: FormDefinition) { const { workspace } = this.app; - let leaf: WorkspaceLeaf | undefined = workspace.getLeavesOfType(viewType)[0]; + let leaf: WorkspaceLeaf | undefined = + workspace.getLeavesOfType(viewType)[0]; if (leaf) { - console.info('found leaf, no reason to create a new one') - } else if (Platform.isMobile || this.settings?.editorPosition === "mainView") { - leaf = this.app.workspace.getLeaf('tab') + console.info("found leaf, no reason to create a new one"); + } else if ( + Platform.isMobile || + this.settings?.editorPosition === "mainView" + ) { + leaf = this.app.workspace.getLeaf("tab"); } else if (this.settings?.editorPosition === "right") { leaf = this.app.workspace.getRightLeaf(false); } else if (this.settings?.editorPosition === "left") { leaf = this.app.workspace.getLeftLeaf(false); } else if (this.settings?.editorPosition === "modal") { - leaf = this.app.workspace.getLeaf(false) + leaf = this.app.workspace.getLeaf(false); } else { - leaf = this.app.workspace.getRightLeaf(false) + leaf = this.app.workspace.getRightLeaf(false); } await leaf.setViewState({ @@ -110,9 +132,7 @@ export default class ModalFormPlugin extends Plugin { active: true, state, }); - this.app.workspace.revealLeaf( - leaf - ); + this.app.workspace.revealLeaf(leaf); return leaf; } @@ -123,18 +143,24 @@ export default class ModalFormPlugin extends Plugin { const [migrationIsNeeded, settings] = pipe( parseSettings(data), E.map((settings): [boolean, ModalFormSettings] => { - const migrationIsNeeded = settings.formDefinitions.some(formNeedsMigration); - const { right: formDefinitions, left: errors } = A.partitionMap(migrateToLatest)(settings.formDefinitions); + const migrationIsNeeded = + settings.formDefinitions.some(formNeedsMigration); + const { right: formDefinitions, left: errors } = A.partitionMap( + migrateToLatest, + )(settings.formDefinitions); notifyParsingErrors(errors); - const validSettings: ModalFormSettings = { ...settings, formDefinitions } - return [migrationIsNeeded, validSettings] + const validSettings: ModalFormSettings = { + ...settings, + formDefinitions, + }; + return [migrationIsNeeded, validSettings]; }), - E.getOrElse(() => [false, getDefaultSettings()]) - ) + E.getOrElse(() => [false, getDefaultSettings()]), + ); if (migrationIsNeeded) { await this.saveSettings(settings); - console.info('Settings were migrated to the latest version') + console.info("Settings were migrated to the latest version"); } return settings; } @@ -155,22 +181,24 @@ export default class ModalFormPlugin extends Plugin { } settingsStore.set(settings); this.unsubscribeSettingsStore = settingsStore.subscribe((s) => { - console.log('settings changed', s) + console.log("settings changed", s); this.settings = s; - this.saveSettings(s) + this.saveSettings(s); }); this.api = new API(this.app, this); - this.registerView(EDIT_FORM_VIEW, (leaf) => new EditFormView(leaf, this)); - this.registerView(MANAGE_FORMS_VIEW, (leaf) => new ManageFormsView(leaf, this)); + this.registerView( + EDIT_FORM_VIEW, + (leaf) => new EditFormView(leaf, this), + ); + this.registerView( + MANAGE_FORMS_VIEW, + (leaf) => new ManageFormsView(leaf, this), + ); // This creates an icon in the left ribbon. - this.addRibbonIcon( - "documents", - "Edit forms", - (evt: MouseEvent) => { - this.manageForms() - } - ); + this.addRibbonIcon("documents", "Edit forms", (evt: MouseEvent) => { + this.manageForms(); + }); this.addCommand({ id: "new-form", @@ -187,12 +215,12 @@ export default class ModalFormPlugin extends Plugin { }, }); this.addCommand({ - id: 'create-note-from-form', - name: 'Create new note from a form', + id: "create-note-from-form", + name: "Create new note from a form", callback: () => { this.createNoteFromForm(); - } - }) + }, + }); // This adds a settings tab so the user can configure various aspects of the plugin this.addSettingTab(new ModalFormSettingTab(this.app, this)); @@ -205,11 +233,19 @@ export default class ModalFormPlugin extends Plugin { * @returns a unique name for the note, full path including the extension */ getUniqueNoteName(name: string, destinationFolder?: string): string { - const defaultNotesFolder = this.app.fileManager.getNewFileParent('', 'note.md') - let destinationPath = `${destinationFolder || defaultNotesFolder.path}/${name}.md` + const defaultNotesFolder = this.app.fileManager.getNewFileParent( + "", + "note.md", + ); + function makePath(name: string, folder?: string, suffix?: number) { + return `${folder || defaultNotesFolder.path}/${name}${ + suffix ? "-" + suffix : "" + }.md`; + } + let destinationPath = makePath(name, destinationFolder); let i = 1; while (file_exists(destinationPath, this.app)) { - destinationPath = `${defaultNotesFolder.path}/${name}-${i}.md` + destinationPath = makePath(name, destinationFolder, i); i++; } return destinationPath; @@ -231,17 +267,32 @@ export default class ModalFormPlugin extends Plugin { return O.some(form as FormWithTemplate); } return O.none; - }) - ) - const onFormSelected = async (form: FormWithTemplate, noteName: string, destinationFolder: string) => { + }), + ); + const onFormSelected = async ( + form: FormWithTemplate, + noteName: string, + destinationFolder: string, + ) => { const formData = await this.api.openForm(form); - const newNoteFullPath = this.getUniqueNoteName(noteName, destinationFolder); - this.app.vault.create(newNoteFullPath, executeTemplate(form.template, formData.getData())) - } - const picker = new NewNoteModal(this.app, formsWithTemplates, ({ form, folder, noteName }) => { - onFormSelected(form, noteName, folder) - }); + const newNoteFullPath = this.getUniqueNoteName( + noteName, + destinationFolder, + ); + const noteContent = executeTemplate( + form.template.parsedTemplate, + formData.getData(), + ); + console.log("new note content", noteContent); + this.app.vault.create(newNoteFullPath, noteContent); + }; + const picker = new NewNoteModal( + this.app, + formsWithTemplates, + ({ form, folder, noteName }) => { + onFormSelected(form, noteName, folder); + }, + ); picker.open(); } - }