From 2fe6f6fc3710eb5545451fde48eb5e485f495974 Mon Sep 17 00:00:00 2001 From: Moritz Jung Date: Thu, 29 Jan 2026 12:19:27 +0100 Subject: [PATCH] bye svelte; welcome solid --- .github/FUNDING.yml | 1 + .github/workflows/release.yml | 8 +- .gitignore | 2 +- automation/build/buildBanner.ts | 26 +- bun.lockb | Bin 187922 -> 211573 bytes eslint.config.mjs | 2 +- package.json | 31 +- src/main.ts | 42 +-- src/settings/Icon.svelte | 39 --- src/settings/Icon.tsx | 24 ++ src/settings/PropertyMapper.ts | 11 +- src/settings/PropertyMapping.ts | 96 ++++++ .../PropertyMappingModelComponent.svelte | 296 ------------------ .../PropertyMappingModelComponent.tsx | 138 ++++++++ .../PropertyMappingModelsComponent.svelte | 24 -- .../PropertyMappingModelsComponent.tsx | 16 + src/settings/Settings.ts | 78 ++--- src/styles.css | 259 +++++++++++++++ styles.css | 75 ----- tsconfig.json | 7 +- vite.config.ts | 80 +++++ 21 files changed, 686 insertions(+), 569 deletions(-) create mode 100644 .github/FUNDING.yml delete mode 100644 src/settings/Icon.svelte create mode 100644 src/settings/Icon.tsx delete mode 100644 src/settings/PropertyMappingModelComponent.svelte create mode 100644 src/settings/PropertyMappingModelComponent.tsx delete mode 100644 src/settings/PropertyMappingModelsComponent.svelte create mode 100644 src/settings/PropertyMappingModelsComponent.tsx create mode 100644 src/styles.css delete mode 100644 styles.css create mode 100644 vite.config.ts diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..4968f5b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: ['mProjectsCode'] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84fe8d9..2594994 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,7 @@ jobs: bun install bun run build mkdir ${{ env.PLUGIN_NAME }} - cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }} + cp dist/main.js dist/manifest.json dist/styles.css ${{ env.PLUGIN_NAME }} zip -r ${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip ${{ env.PLUGIN_NAME }} ls @@ -49,6 +49,6 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} files: | ${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip - main.js - manifest.json - styles.css + dist/main.js + dist/manifest.json + dist/styles.css diff --git a/.gitignore b/.gitignore index 2c1c4e4..80d9bf9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ exampleVault/.obsidian/plugins/obsidian-media-db-plugin/* exampleVault/Media DB/* meta.txt -main.js \ No newline at end of file +dist/ \ No newline at end of file diff --git a/automation/build/buildBanner.ts b/automation/build/buildBanner.ts index b8a62cf..849aec3 100644 --- a/automation/build/buildBanner.ts +++ b/automation/build/buildBanner.ts @@ -1,4 +1,4 @@ -import manifest from '../../manifest.json' assert { type: 'json' }; +import manifest from '../../manifest.json' with { type: 'json' }; export function getBuildBanner(buildType: string, getVersion: (version: string) => string) { return `/* @@ -9,30 +9,16 @@ By: ${manifest.author} (${manifest.authorUrl}) Time: ${new Date().toUTCString()} Version: ${getVersion(manifest.version)} ------------------------------------------- -THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +THIS IS A GENERATED/BUNDLED FILE if you want to view the source, please visit the github repository of this plugin ------------------------------------------- -MIT License - Copyright (c) ${new Date().getFullYear()} ${manifest.author} +------------------------------------------- +This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +You should have received a copy of the GNU General Public License along with this program. If not, see . */ `; } diff --git a/bun.lockb b/bun.lockb index f7a1bfcfb80075b55d3061b0138f7361173df187..419cad865cc97c68424cbbc91486c024ac6b6702 100755 GIT binary patch delta 59904 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 bthh>s$?W`l8IGbwCLBe84f4FP+;HxH)s1rg diff --git a/eslint.config.mjs b/eslint.config.mjs index 8d66d47..c634b74 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,7 @@ import * as plugin_import from 'eslint-plugin-import'; export default tseslint.config( { - ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte', 'src/api/schemas/'], + ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.css', 'src/api/schemas/'], }, { files: ['src/**/*.ts'], diff --git a/package.json b/package.json index 2fbc05c..92d08d1 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,17 @@ "description": "A plugin that can query multiple APIs for movies, series, anime, manga, books, comics, games, music and wiki articles, and import them into your vault.", "main": "main.js", "scripts": { - "dev": "bun run automation/build/esbuild.dev.config.ts", - "build": "bun run tsc && bun run automation/build/esbuild.config.ts", + "dev": "vite build --watch --mode development", + "build": "bun run tsc && vite build --mode production", "tsc": "tsc -noEmit -skipLibCheck", "test": "bun test", "test:log": "LOG_TESTS=true bun test", - "format": "prettier --write --plugin prettier-plugin-svelte .", - "format:check": "prettier --check --plugin prettier-plugin-svelte .", - "lint": "eslint --max-warnings=0 src/**", - "lint:fix": "eslint --max-warnings=0 --fix src/**", - "svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"", - "check": "bun run format:check && bun run tsc && bun run lint && bun run svelte-check", - "check:fix": "bun run format && bun run tsc && bun run lint:fix && bun run svelte-check", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "eslint --max-warnings=0 --no-warn-ignored src/**", + "lint:fix": "eslint --max-warnings=0 --fix --no-warn-ignored src/**", + "check": "bun run format:check && bun run tsc && bun run lint", + "check:fix": "bun run format && bun run tsc && bun run lint:fix", "release": "bun run automation/release.ts", "stats": "bun run automation/stats.ts" }, @@ -28,9 +27,6 @@ "@popperjs/core": "^2.11.8", "@types/bun": "^1.3.7", "builtin-modules": "^5.0.0", - "esbuild": "^0.25.12", - "esbuild-plugin-copy-watch": "^2.3.1", - "esbuild-svelte": "^0.9.4", "eslint": "^9.39.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-only-warn": "^1.1.0", @@ -39,13 +35,14 @@ "openapi-fetch": "^0.14.1", "openapi-typescript": "^7.10.1", "prettier": "^3.8.1", - "prettier-plugin-svelte": "^3.4.1", + "solid-js": "^1.9.3", "string-argv": "^0.3.2", - "svelte": "^5.48.5", - "svelte-check": "^4.3.5", - "svelte-preprocess": "^6.0.3", "tslib": "^2.8.1", "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0" + "typescript-eslint": "^8.54.0", + "vite": "^6.0.5", + "vite-plugin-banner": "^0.8.1", + "vite-plugin-solid": "^2.11.0", + "vite-plugin-static-copy": "^3.2.0" } } diff --git a/src/main.ts b/src/main.ts index 78d2173..ce08a21 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,11 +19,11 @@ import { TMDBSeriesAPI } from './api/apis/TMDBSeriesAPI'; import { VNDBAPI } from './api/apis/VNDBAPI'; import { WikipediaAPI } from './api/apis/WikipediaAPI'; import { ConfirmOverwriteModal } from './modals/ConfirmOverwriteModal'; -import type {SeasonSelectModalElement} from './modals/MediaDbSeasonSelectModal'; -import { MediaDbSeasonSelectModal } from './modals/MediaDbSeasonSelectModal'; +import type { SeasonSelectModalElement } from './modals/MediaDbSeasonSelectModal'; +import { MediaDbSeasonSelectModal } from './modals/MediaDbSeasonSelectModal'; import type { MediaTypeModel } from './models/MediaTypeModel'; import { PropertyMapper } from './settings/PropertyMapper'; -import { PropertyMapping, PropertyMappingModel } from './settings/PropertyMapping'; +import { PropertyMappingModel } from './settings/PropertyMapping'; import type { MediaDbPluginSettings } from './settings/Settings'; import { getDefaultSettings, MediaDbSettingTab } from './settings/Settings'; import { BulkImportHelper } from './utils/BulkImportHelper'; @@ -33,6 +33,7 @@ import type { SearchModalOptions } from './utils/ModalHelper'; import { ModalHelper } from './utils/ModalHelper'; import type { CreateNoteOptions } from './utils/Utils'; import { replaceIllegalFileNameCharactersInString, unCamelCase, hasTemplaterPlugin, useTemplaterPluginInFile } from './utils/Utils'; +import 'src/styles.css'; export type Metadata = Record; @@ -652,39 +653,18 @@ export default class MediaDbPlugin extends Plugin { } async loadSettings(): Promise { - // console.log(DEFAULT_SETTINGS); const diskSettings: MediaDbPluginSettings = (await this.loadData()) as MediaDbPluginSettings; const defaultSettings: MediaDbPluginSettings = getDefaultSettings(this); const loadedSettings: MediaDbPluginSettings = Object.assign({}, defaultSettings, diskSettings); - // migrate the settings loaded from the disk to match the structure of the default settings - const newPropertyMappings: PropertyMappingModel[] = []; - for (const defaultPropertyMappingModel of defaultSettings.propertyMappingModels) { - const newPropertyMappingModel = loadedSettings.propertyMappingModels.find(x => x.type === defaultPropertyMappingModel.type); - if (newPropertyMappingModel === undefined) { - // if the propertyMappingModel exists in the default settings but not the loaded settings, add it - newPropertyMappings.push(defaultPropertyMappingModel); - } else { - // if the propertyMappingModel also exists in the loaded settings, add it from there - const newProperties: PropertyMapping[] = []; + // Migrate property mappings using the dedicated migration method + const migratedModels = PropertyMappingModel.migrateModels( + loadedSettings.propertyMappingModels || [], + defaultSettings.propertyMappingModels.map(m => PropertyMappingModel.fromJSON(m)), + ); - for (const defaultProperty of defaultPropertyMappingModel.properties) { - const newProperty = newPropertyMappingModel.properties.find(x => x.property === defaultProperty.property); - if (newProperty === undefined) { - // default property is an instance - newProperties.push(defaultProperty); - } else { - // newProperty is just an object and take locked status from default property - newProperties.push( - new PropertyMapping(newProperty.property, newProperty.newProperty, newProperty.mapping, defaultProperty.locked, newProperty.wikilink ?? false), - ); - } - } - - newPropertyMappings.push(new PropertyMappingModel(newPropertyMappingModel.type, newProperties)); - } - } - loadedSettings.propertyMappingModels = newPropertyMappings; + // Store as plain data for serialization + loadedSettings.propertyMappingModels = migratedModels.map(m => m.toJSON()); this.settings = loadedSettings; } diff --git a/src/settings/Icon.svelte b/src/settings/Icon.svelte deleted file mode 100644 index a73d4a4..0000000 --- a/src/settings/Icon.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - - -{#if iconName.length > 0} -

-
-
-{/if} - - diff --git a/src/settings/Icon.tsx b/src/settings/Icon.tsx new file mode 100644 index 0000000..9723197 --- /dev/null +++ b/src/settings/Icon.tsx @@ -0,0 +1,24 @@ +import { onMount, Show } from 'solid-js'; +import { setIcon } from 'obsidian'; + +interface IconProps { + iconName?: string; +} + +export default function Icon(props: IconProps) { + let iconEl: HTMLDivElement | undefined; + + onMount(() => { + if (iconEl) { + setIcon(iconEl, props.iconName || ''); + } + }); + + return ( + 0}> +
+
+
+
+ ); +} diff --git a/src/settings/PropertyMapper.ts b/src/settings/PropertyMapper.ts index 9ca5e0c..bf08d32 100644 --- a/src/settings/PropertyMapper.ts +++ b/src/settings/PropertyMapper.ts @@ -27,8 +27,12 @@ export class PropertyMapper { return obj; } - // @ts-ignore - const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type).properties; + const propertyMappingModel = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type); + if (!propertyMappingModel) { + return obj; + } + + const propertyMappings = propertyMappingModel.properties; const newObj: Record = {}; @@ -80,7 +84,8 @@ export class PropertyMapper { return obj; } - const propertyMappings = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type)?.properties ?? []; + const propertyMappingModel = this.plugin.settings.propertyMappingModels.find(x => x.type === obj.type); + const propertyMappings = propertyMappingModel?.properties ?? []; const originalObj: Record = {}; diff --git a/src/settings/PropertyMapping.ts b/src/settings/PropertyMapping.ts index de4adaa..82d044e 100644 --- a/src/settings/PropertyMapping.ts +++ b/src/settings/PropertyMapping.ts @@ -1,6 +1,20 @@ import type { MediaType } from '../utils/MediaType'; import { containsOnlyLettersAndUnderscores, PropertyMappingNameConflictError, PropertyMappingValidationError } from '../utils/Utils'; +// Plain object interfaces for serialization +export interface PropertyMappingData { + property: string; + newProperty: string; + mapping: PropertyMappingOption; + locked?: boolean; + wikilink?: boolean; +} + +export interface PropertyMappingModelData { + type: MediaType; + properties: PropertyMappingData[]; +} + export enum PropertyMappingOption { Default = 'default', Map = 'remap', @@ -80,6 +94,72 @@ export class PropertyMappingModel { } return copy; } + + // Serialization - returns a plain object that can be JSON.stringify'd + toJSON(): PropertyMappingModelData { + return { + type: this.type, + properties: this.properties.map(p => p.toJSON()), + }; + } + + // Deserialization - creates a PropertyMappingModel from a plain object + static fromJSON(json: PropertyMappingModelData): PropertyMappingModel { + return new PropertyMappingModel( + json.type, + json.properties.map(p => PropertyMapping.fromJSON(p)), + ); + } + + /** + * Migrates loaded settings to match the structure of default settings. + * - Adds new properties from defaults that don't exist in loaded settings + * - Preserves user customizations from loaded settings + * - Updates locked status from defaults + * + * @param loadedModels - Models loaded from disk (may be outdated) + * @param defaultModels - Current default models (source of truth for structure) + * @returns Migrated models with correct structure and preserved user settings + */ + static migrateModels(loadedModels: PropertyMappingModelData[], defaultModels: PropertyMappingModel[]): PropertyMappingModel[] { + const migratedModels: PropertyMappingModel[] = []; + + for (const defaultModel of defaultModels) { + const loadedModel = loadedModels.find(m => m.type === defaultModel.type); + + if (!loadedModel) { + // New model type - use default + migratedModels.push(defaultModel); + continue; + } + + // Migrate properties + const migratedProperties: PropertyMapping[] = []; + for (const defaultProperty of defaultModel.properties) { + const loadedProperty = loadedModel.properties.find(p => p.property === defaultProperty.property); + + if (!loadedProperty) { + // New property - use default + migratedProperties.push(defaultProperty); + } else { + // Existing property - merge: take locked from default, customizations from loaded + migratedProperties.push( + new PropertyMapping( + loadedProperty.property, + loadedProperty.newProperty, + loadedProperty.mapping, + defaultProperty.locked, // locked status from default + loadedProperty.wikilink ?? false, + ), + ); + } + } + + migratedModels.push(new PropertyMappingModel(defaultModel.type, migratedProperties)); + } + + return migratedModels; + } } export class PropertyMapping { @@ -153,4 +233,20 @@ export class PropertyMapping { return this.property; } + + // Serialization - returns a plain object + toJSON(): PropertyMappingData { + return { + property: this.property, + newProperty: this.newProperty, + mapping: this.mapping, + locked: this.locked, + wikilink: this.wikilink, + }; + } + + // Deserialization - creates a PropertyMapping from a plain object + static fromJSON(json: PropertyMappingData): PropertyMapping { + return new PropertyMapping(json.property, json.newProperty, json.mapping, json.locked, json.wikilink); + } } diff --git a/src/settings/PropertyMappingModelComponent.svelte b/src/settings/PropertyMappingModelComponent.svelte deleted file mode 100644 index 064eec5..0000000 --- a/src/settings/PropertyMappingModelComponent.svelte +++ /dev/null @@ -1,296 +0,0 @@ - - -
-
-
{capitalizeFirstLetter(model.type)}
- -
- {#if unsavedChanges} -
Unsaved changes
- {/if} - - -
-
- - {#if !validationResult?.res} -
- {validationResult?.err?.message} -
- {/if} - -
- - - - - - - - - - - {#each model.properties as property} - - - - {#if property.locked} - - {:else} - - - - - - {/if} - - {/each} - -
PropertyMappingNew nameWikilink
- {property.property} - -
property cannot be remapped
-
- - - {#if property.mapping === PropertyMappingOption.Map} -
- - onModelUpdate()} - /> -
- {:else} - - {/if} -
-
-
- - diff --git a/src/settings/PropertyMappingModelComponent.tsx b/src/settings/PropertyMappingModelComponent.tsx new file mode 100644 index 0000000..d9a9017 --- /dev/null +++ b/src/settings/PropertyMappingModelComponent.tsx @@ -0,0 +1,138 @@ +import { createSignal, createMemo, For, Show } from 'solid-js'; +import { createStore } from 'solid-js/store'; +import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions, type PropertyMappingModelData } from './PropertyMapping'; +import { capitalizeFirstLetter } from '../utils/Utils'; +import Icon from './Icon'; + +interface PropertyMappingModelComponentProps { + model: PropertyMappingModelData; + save: (model: PropertyMappingModelData) => void; +} + +export default function PropertyMappingModelComponent(props: PropertyMappingModelComponentProps) { + const [unsavedChanges, setUnsavedChanges] = createSignal(false); + + // Create a store from the model's plain data + const [modelData, setModelData] = createStore(props.model); + + // Derive the validation result reactively + const validationResult = createMemo(() => { + const model = PropertyMappingModel.fromJSON(modelData); + return model.validate(); + }); + + const onModelUpdate = () => { + setUnsavedChanges(true); + }; + + const handleSave = () => { + const model = PropertyMappingModel.fromJSON(modelData); + if (model.validate().res) { + props.save(model); + setUnsavedChanges(false); + } + }; + + return ( +
+
+
{capitalizeFirstLetter(modelData.type)}
+ +
+ +
Unsaved changes
+
+ + +
+
+ + +
{validationResult().err?.message}
+
+ +
+ + + + + + + + + + + + {(property, index) => ( + + + + +
property cannot be remapped
+ + } + > +
+ + + + + + + )} + + +
PropertyMappingNew nameWikilink
+ {property.property} + + + + —} + > +
+ + { + setModelData('properties', index(), 'newProperty', e.currentTarget.value); + onModelUpdate(); + }} + /> +
+
+
+
+
+ ); +} diff --git a/src/settings/PropertyMappingModelsComponent.svelte b/src/settings/PropertyMappingModelsComponent.svelte deleted file mode 100644 index edfd197..0000000 --- a/src/settings/PropertyMappingModelsComponent.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
- {#each models as model} - - {/each} - - -
- - diff --git a/src/settings/PropertyMappingModelsComponent.tsx b/src/settings/PropertyMappingModelsComponent.tsx new file mode 100644 index 0000000..35274ff --- /dev/null +++ b/src/settings/PropertyMappingModelsComponent.tsx @@ -0,0 +1,16 @@ +import { For } from 'solid-js'; +import { type PropertyMappingModelData } from './PropertyMapping'; +import PropertyMappingModelComponent from './PropertyMappingModelComponent'; + +interface PropertyMappingModelsComponentProps { + models?: PropertyMappingModelData[]; + save: (model: PropertyMappingModelData) => void; +} + +export default function PropertyMappingModelsComponent(props: PropertyMappingModelsComponentProps) { + return ( +
+ {model => } +
+ ); +} diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index e82ef8b..4c24635 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -1,13 +1,14 @@ import type { App } from 'obsidian'; import { Notice, PluginSettingTab, SettingGroup } from 'obsidian'; +import { render } from 'solid-js/web'; import { MediaType } from 'src/utils/MediaType'; -import { mount } from 'svelte'; import type MediaDbPlugin from '../main'; import type { MediaTypeModel } from '../models/MediaTypeModel'; import { MEDIA_TYPES } from '../utils/MediaTypeManager'; import { fragWithHTML, unCamelCase } from '../utils/Utils'; +import type { PropertyMappingModelData } from './PropertyMapping'; import { PropertyMapping, PropertyMappingModel, PropertyMappingOption } from './PropertyMapping'; -import PropertyMappingModelsComponent from './PropertyMappingModelsComponent.svelte'; +import PropertyMappingModelsComponent from './PropertyMappingModelsComponent'; import { FileSuggest } from './suggesters/FileSuggest'; import { FolderSuggest } from './suggesters/FolderSuggest'; @@ -74,7 +75,7 @@ export interface MediaDbPluginSettings { boardgameFolder: string; bookFolder: string; - propertyMappingModels: PropertyMappingModel[]; + propertyMappingModels: PropertyMappingModelData[]; // DEPRECATED: Use propertyMappingModels instead moviePropertyConversionRules: string; @@ -347,12 +348,10 @@ export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings const defaultSettings = DEFAULT_SETTINGS; // construct property mapping defaults - const propertyMappingModels: PropertyMappingModel[] = []; + const propertyMappingModels: PropertyMappingModelData[] = []; for (const mediaType of MEDIA_TYPES) { const model: MediaTypeModel = plugin.mediaTypeManager.createMediaTypeModelFromMediaType({}, mediaType); const metadataObj = model.toMetaDataObject(); - // console.log(metadataObj); - // console.log(model); const propertyMappingModel: PropertyMappingModel = new PropertyMappingModel(mediaType); @@ -368,43 +367,14 @@ export function getDefaultSettings(plugin: MediaDbPlugin): MediaDbPluginSettings ); } - propertyMappingModels.push(propertyMappingModel); - } - - // MIGRATION: Ensure all property mappings have wikilink defined (for settings loaded from disk) - if (defaultSettings.propertyMappingModels && Array.isArray(defaultSettings.propertyMappingModels)) { - for (const model of defaultSettings.propertyMappingModels) { - if (model.properties && Array.isArray(model.properties)) { - for (const prop of model.properties) { - if (typeof prop.wikilink === 'undefined') { - prop.wikilink = false; - } - } - } - } + // Convert to plain data for serialization + propertyMappingModels.push(propertyMappingModel.toJSON()); } defaultSettings.propertyMappingModels = propertyMappingModels; return defaultSettings; } -/** - * Ensures all property mappings in loaded settings have the wikilink property defined. - */ -export function ensureWikilinkOnPropertyMappings(settings: MediaDbPluginSettings): void { - if (settings.propertyMappingModels && Array.isArray(settings.propertyMappingModels)) { - for (const model of settings.propertyMappingModels) { - if (model.properties && Array.isArray(model.properties)) { - for (const prop of model.properties) { - if (typeof prop.wikilink === 'undefined') { - prop.wikilink = false; - } - } - } - } - } -} - // MARK: Settings Tab export class MediaDbSettingTab extends PluginSettingTab { plugin: MediaDbPlugin; @@ -787,27 +757,23 @@ export class MediaDbSettingTab extends PluginSettingTab { ), ); - mount(PropertyMappingModelsComponent, { - target: setting.descEl, - props: { - models: this.plugin.settings.propertyMappingModels.map(x => x.copy()), - save: (model: PropertyMappingModel): void => { - const propertyMappingModels: PropertyMappingModel[] = []; - - for (const model2 of this.plugin.settings.propertyMappingModels) { - if (model2.type === model.type) { - propertyMappingModels.push(model); - } else { - propertyMappingModels.push(model2); + render( + () => + PropertyMappingModelsComponent({ + models: structuredClone(this.plugin.settings.propertyMappingModels), + save: (model: PropertyMappingModelData): void => { + // Update the matching model in settings (stored as plain data) + const index = this.plugin.settings.propertyMappingModels.findIndex(m => m.type === model.type); + if (index !== -1) { + this.plugin.settings.propertyMappingModels[index] = model; } - } - this.plugin.settings.propertyMappingModels = propertyMappingModels; - new Notice(`MDB: Property mappings for ${model.type} saved successfully.`); - void this.plugin.saveSettings(); - }, - }, - }); + new Notice(`MDB: Property mappings for ${model.type} saved successfully.`); + void this.plugin.saveSettings(); + }, + }), + setting.descEl, + ); }); } } diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..b204d1d --- /dev/null +++ b/src/styles.css @@ -0,0 +1,259 @@ +.media-db-plugin-list-wrapper { + display: flex; + align-content: center; + margin-bottom: 5px; + margin-top: 5px; +} + +.media-db-plugin-list-toggle { +} + +.media-db-plugin-list-text-wrapper { + flex: 1; +} + +.media-db-plugin-list-text { + display: block; +} + +small.media-db-plugin-list-text { + color: var(--text-muted); +} + +.media-db-plugin-select-modal { + display: contents; +} + +.media-db-plugin-select-wrapper { + display: flex; + flex-direction: column; + margin: 5px; + overflow-y: auto; +} + +.media-db-plugin-select-element { + cursor: pointer; + border-left: 5px solid transparent; + padding: 5px; + margin: 5px 0 5px 0; + border-radius: 5px; + white-space: pre-wrap; + font-size: 16px; +} + +.media-db-plugin-select-element-selected { + border-left: 5px solid var(--interactive-accent) !important; + background: var(--background-secondary-alt); +} + +.media-db-plugin-select-element-hover { + background: var(--background-secondary-alt); +} + +.media-db-plugin-preview-modal { + display: contents; +} + +.media-db-plugin-preview-wrapper { + display: flex; + flex-direction: column; + overflow-y: auto; +} + +.media-db-plugin-spacer { + margin-bottom: 10px; +} + +.media-db-plugin-button:focus { + /*outline: 1px solid white;*/ +} + +.media-db-plugin-preview { + border-radius: var(--modal-radius); + border: var(--modal-border-width) solid var(--modal-border-color); + padding: var(--size-4-4); +} + +/* Icon Component Styles */ +.icon-wrapper { + display: inline-block; + position: relative; + width: 20px; +} + +.icon { + position: absolute; + height: 20px; + width: 20px; + top: calc(50% - 10px); +} + +/* Property Mapping Component Styles */ +.media-db-plugin-property-mappings-model-container { + margin-bottom: var(--size-4-8); +} + +.media-db-plugin-property-mappings-model-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: var(--size-4-4); + gap: var(--size-4-3); +} + +.media-db-plugin-property-mappings-model-header .setting-item-name { + font-weight: var(--font-semibold); + font-size: var(--font-ui-medium); + color: var(--text-normal); + margin: 0; +} + +.media-db-plugin-property-mappings-model-actions { + display: flex; + align-items: center; + gap: var(--size-4-3); +} + +.media-db-plugin-property-mapping-unsaved-changes { + color: var(--text-warning); + font-size: var(--font-ui-small); + white-space: nowrap; +} + +.media-db-plugin-property-mappings-save-button { + white-space: nowrap; + cursor: pointer; +} + +.media-db-plugin-property-mappings-save-button.mod-muted { + opacity: 0.5; + cursor: not-allowed; +} + +.media-db-plugin-property-mapping-validation { + color: var(--text-error); + background: rgba(var(--color-red-rgb), 0.1); + padding: var(--size-4-3) var(--size-4-4); + margin-bottom: var(--size-4-4); + border-left: 3px solid var(--text-error); + font-size: var(--font-ui-small); + line-height: 1.5; + border-radius: var(--radius-s); +} + +.media-db-plugin-property-mappings-table-container { + overflow-x: auto; +} + +.media-db-plugin-property-mappings-table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + font-size: var(--font-ui-small); +} + +.media-db-plugin-property-mappings-table thead { + border-bottom: 1px solid var(--background-modifier-border); +} + +.media-db-plugin-property-mappings-table th { + padding: var(--size-4-2) var(--size-4-3); + padding-left: 0; + text-align: left; + font-weight: var(--font-semibold); + color: var(--text-muted); + font-size: var(--font-ui-smaller); + text-transform: uppercase; + letter-spacing: 0.02em; + border-bottom: none; +} + +.media-db-plugin-property-mappings-table tbody tr { + transition: background-color 0.1s ease; +} + +.media-db-plugin-property-mappings-table td { + padding: var(--size-4-3) var(--size-4-3) var(--size-4-3) 0; + border-bottom: 1px solid var(--background-modifier-border-hover); + vertical-align: middle; +} + +.media-db-plugin-property-mappings-table tbody tr:last-child td { + border-bottom: none; +} + +.col-property { + width: 25%; + white-space: nowrap; +} + +.col-mapping { + width: 20%; +} + +.col-new-name { + width: 40%; +} + +.col-wikilink { + width: 15%; + text-align: center; +} + +.col-locked { + text-align: center; + font-style: italic; +} + +.media-db-plugin-property-mappings-table code { + padding: var(--size-4-1) var(--size-4-2); + margin: 0; + background: var(--code-background); + color: var(--code-normal); + border-radius: var(--radius-s); + font-size: var(--font-ui-smaller); + font-family: var(--font-monospace); +} + +.media-db-plugin-property-binding-text { + color: var(--text-muted); + font-size: var(--font-ui-small); + font-style: italic; +} + +.media-db-plugin-property-mappings-table select.dropdown { + width: 100%; + max-width: 100%; +} + +.media-db-plugin-property-mapping-to { + display: flex; + align-items: center; + gap: var(--size-4-2); + min-width: 0; +} + +.media-db-plugin-property-mapping-input { + flex: 1; + width: 100%; + font-family: var(--font-monospace); +} + +.media-db-plugin-property-mapping-to-disabled { + color: var(--text-faint); + font-size: var(--font-ui-medium); +} + +.media-db-plugin-property-mapping-wikilink-label { + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: var(--size-4-1); +} + +.media-db-plugin-property-mapping-wikilink-label input[type='checkbox'] { + cursor: pointer; + width: var(--checkbox-size); + height: var(--checkbox-size); +} diff --git a/styles.css b/styles.css deleted file mode 100644 index b7351e4..0000000 --- a/styles.css +++ /dev/null @@ -1,75 +0,0 @@ -.media-db-plugin-list-wrapper { - display: flex; - align-content: center; - margin-bottom: 5px; - margin-top: 5px; -} - -.media-db-plugin-list-toggle { -} - -.media-db-plugin-list-text-wrapper { - flex: 1; -} - -.media-db-plugin-list-text { - display: block; -} - -small.media-db-plugin-list-text { - color: var(--text-muted); -} - -.media-db-plugin-select-modal { - display: contents; -} - -.media-db-plugin-select-wrapper { - display: flex; - flex-direction: column; - margin: 5px; - overflow-y: auto; -} - -.media-db-plugin-select-element { - cursor: pointer; - border-left: 5px solid transparent; - padding: 5px; - margin: 5px 0 5px 0; - border-radius: 5px; - white-space: pre-wrap; - font-size: 16px; -} - -.media-db-plugin-select-element-selected { - border-left: 5px solid var(--interactive-accent) !important; - background: var(--background-secondary-alt); -} - -.media-db-plugin-select-element-hover { - background: var(--background-secondary-alt); -} - -.media-db-plugin-preview-modal { - display: contents; -} - -.media-db-plugin-preview-wrapper { - display: flex; - flex-direction: column; - overflow-y: auto; -} - -.media-db-plugin-spacer { - margin-bottom: 10px; -} - -.media-db-plugin-button:focus { - /*outline: 1px solid white;*/ -} - -.media-db-plugin-preview { - border-radius: var(--modal-radius); - border: var(--modal-border-width) solid var(--modal-border-color); - padding: var(--size-4-4); -} diff --git a/tsconfig.json b/tsconfig.json index 9825095..c6eb672 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,10 @@ "moduleDetection": "force", "sourceMap": true, "lib": ["DOM", "ESNext"], - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"] }, - "include": ["src/**/*.ts", "tests/**/*.ts"] + "include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"] } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ffa6b85 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,80 @@ +import { defineConfig } from 'vite'; +import solidPlugin from 'vite-plugin-solid'; +import builtins from 'builtin-modules'; +import { getBuildBanner } from './automation/build/buildBanner'; +import { viteStaticCopy } from 'vite-plugin-static-copy'; +import banner from 'vite-plugin-banner'; +import manifest from './manifest.json' with { type: 'json' }; +import path from 'path'; + +const entryFile = 'src/main.ts'; + +export default defineConfig(({ mode }) => { + const prod = mode === 'production'; + const outDir = prod ? 'dist/' : `exampleVault/.obsidian/plugins/${manifest.id}`; + + const plugins = [ + solidPlugin(), + banner({ + outDir: outDir, + content: getBuildBanner(prod ? 'Release Build' : 'Dev Build', version => version), + }), + viteStaticCopy({ + targets: [ + { + src: 'manifest.json', + dest: outDir, + }, + ], + }), + ]; + + return { + plugins, + + resolve: { + alias: { + src: path.resolve(__dirname, './src'), + }, + }, + build: { + lib: { + entry: path.resolve(__dirname, entryFile), + name: 'main', + fileName: () => 'main.js', + formats: ['cjs'], + }, + minify: prod, + sourcemap: prod ? false : 'inline', + cssCodeSplit: false, + emptyOutDir: false, + outDir: '', + rollupOptions: { + input: { + main: path.resolve(__dirname, entryFile), + }, + output: { + dir: outDir, + entryFileNames: 'main.js', + assetFileNames: 'styles.css', + }, + external: [ + 'obsidian', + 'electron', + '@codemirror/autocomplete', + '@codemirror/collab', + '@codemirror/commands', + '@codemirror/language', + '@codemirror/lint', + '@codemirror/search', + '@codemirror/state', + '@codemirror/view', + '@lezer/common', + '@lezer/highlight', + '@lezer/lr', + ...builtins, + ], + }, + }, + }; +});