update settings; fix some minor issues
This commit is contained in:
parent
abbf10cf3d
commit
97cf350a32
24 changed files with 882 additions and 950 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -36,4 +36,5 @@ exampleVault/.obsidian/plugins/obsidian-media-db-plugin/*
|
|||
!exampleVault/.obsidian/plugins/obsidian-media-db-plugin/.hotreload
|
||||
|
||||
exampleVault/Media DB/*
|
||||
meta.txt
|
||||
meta.txt
|
||||
main.js
|
||||
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
24
package.json
24
package.json
|
|
@ -26,26 +26,26 @@
|
|||
"@happy-dom/global-registrator": "^18.0.1",
|
||||
"@lemons_dev/parsinom": "^0.0.12",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/bun": "^1.2.19",
|
||||
"@types/bun": "^1.3.7",
|
||||
"builtin-modules": "^5.0.0",
|
||||
"esbuild": "^0.25.8",
|
||||
"esbuild": "^0.25.12",
|
||||
"esbuild-plugin-copy-watch": "^2.3.1",
|
||||
"esbuild-svelte": "^0.9.3",
|
||||
"eslint": "^9.32.0",
|
||||
"esbuild-svelte": "^0.9.4",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"iso-639-2": "^3.0.2",
|
||||
"obsidian": "latest",
|
||||
"openapi-fetch": "^0.14.0",
|
||||
"openapi-typescript": "^7.8.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"openapi-fetch": "^0.14.1",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"prettier": "^3.8.1",
|
||||
"prettier-plugin-svelte": "^3.4.1",
|
||||
"string-argv": "^0.3.2",
|
||||
"svelte": "^5.38.0",
|
||||
"svelte-check": "^4.3.1",
|
||||
"svelte": "^5.48.5",
|
||||
"svelte-check": "^4.3.5",
|
||||
"svelte-preprocess": "^6.0.3",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.0"
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.54.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
import { SeasonModel } from '../../models/SeasonModel';
|
||||
import { MediaType } from '../../utils/MediaType';
|
||||
import { APIModel } from '../APIModel';
|
||||
import { SeasonModel } from '../../models/SeasonModel';
|
||||
import type { paths } from '../schemas/TMDB';
|
||||
|
||||
export class TMDBSeasonAPI extends APIModel {
|
||||
|
|
@ -82,7 +84,9 @@ export class TMDBSeasonAPI extends APIModel {
|
|||
totalSeasons = detailsData.seasons.length;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
// Ignore errors and assume 0 seasons
|
||||
}
|
||||
|
||||
ret.push(
|
||||
new SeasonModel({
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call */
|
||||
|
||||
import createClient from 'openapi-fetch';
|
||||
import type MediaDbPlugin from '../../main';
|
||||
import type { MediaTypeModel } from '../../models/MediaTypeModel';
|
||||
|
|
|
|||
|
|
@ -257,4 +257,8 @@ export class VNDBAPI extends APIModel {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
getDisabledMediaTypes(): MediaType[] {
|
||||
return this.plugin.settings.VNDBAPI_disabledMediaTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/main.ts
19
src/main.ts
|
|
@ -13,15 +13,14 @@ import { MusicBrainzAPI } from './api/apis/MusicBrainzAPI';
|
|||
import { OMDbAPI } from './api/apis/OMDbAPI';
|
||||
import { OpenLibraryAPI } from './api/apis/OpenLibraryAPI';
|
||||
import { SteamAPI } from './api/apis/SteamAPI';
|
||||
import { TMDBSeriesAPI } from './api/apis/TMDBSeriesAPI';
|
||||
import { TMDBSeasonAPI } from './api/apis/TMDBSeasonAPI';
|
||||
import { TMDBMovieAPI } from './api/apis/TMDBMovieAPI';
|
||||
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
||||
import { ComicVineAPI } from './api/apis/ComicVineAPI';
|
||||
import { TMDBSeasonAPI } from './api/apis/TMDBSeasonAPI';
|
||||
import { TMDBSeriesAPI } from './api/apis/TMDBSeriesAPI';
|
||||
import { VNDBAPI } from './api/apis/VNDBAPI';
|
||||
import { MediaDbFolderImportModal } from './modals/MediaDbFolderImportModal';
|
||||
import { WikipediaAPI } from './api/apis/WikipediaAPI';
|
||||
import { ConfirmOverwriteModal } from './modals/ConfirmOverwriteModal';
|
||||
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';
|
||||
|
|
@ -271,7 +270,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
private async handleSeasonSelectModal(types: string[], selectResults: MediaTypeModel[]): Promise<boolean> {
|
||||
if (types.length === 1 && types[0] === 'season' && selectResults.length === 1 && selectResults[0].dataSource === 'TMDBSeasonAPI') {
|
||||
// Use static import for the modal
|
||||
const tmdbSeasonAPI = this.apiManager.getApiByName('TMDBSeasonAPI') as import('./api/apis/TMDBSeasonAPI').TMDBSeasonAPI;
|
||||
const tmdbSeasonAPI = this.apiManager.getApiByName('TMDBSeasonAPI') as TMDBSeasonAPI;
|
||||
if (!tmdbSeasonAPI) {
|
||||
new Notice('TMDBSeasonAPI not found.');
|
||||
return true;
|
||||
|
|
@ -296,8 +295,8 @@ export default class MediaDbPlugin extends Plugin {
|
|||
true,
|
||||
seriesName,
|
||||
);
|
||||
const selectedSeasons: any[] = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(resolve);
|
||||
const selectedSeasons: SeasonSelectModalElement[] = await new Promise(resolve => {
|
||||
modal.setSubmitCb(resolve);
|
||||
modal.open();
|
||||
});
|
||||
if (!selectedSeasons || selectedSeasons.length === 0) {
|
||||
|
|
@ -309,7 +308,7 @@ export default class MediaDbPlugin extends Plugin {
|
|||
const orig = allSeasons.find(s => s.seasonNumber === season.season_number);
|
||||
if (orig) {
|
||||
// Fetch full metadata using getById
|
||||
const tmdbSeasonAPI = this.apiManager.getApiByName('TMDBSeasonAPI') as import('./api/apis/TMDBSeasonAPI').TMDBSeasonAPI;
|
||||
const tmdbSeasonAPI = this.apiManager.getApiByName('TMDBSeasonAPI') as TMDBSeasonAPI;
|
||||
if (tmdbSeasonAPI) {
|
||||
const fullMeta = await tmdbSeasonAPI.getById(orig.id);
|
||||
await this.createMediaDbNotes([fullMeta]);
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ export class MediaDbAdvancedSearchModal extends Modal {
|
|||
this.isBusy = false;
|
||||
}
|
||||
|
||||
setSubmitCallback(submitCallback: (res: AdvancedSearchModalData) => void): void {
|
||||
setSubmitCb(submitCallback: (res: AdvancedSearchModalData) => void): void {
|
||||
this.submitCallback = submitCallback;
|
||||
}
|
||||
|
||||
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||
setCloseCb(closeCallback: (err?: Error) => void): void {
|
||||
this.closeCallback = closeCallback;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@ export class MediaDbIdSearchModal extends Modal {
|
|||
this.isBusy = false;
|
||||
}
|
||||
|
||||
setSubmitCallback(submitCallback: (res: IdSearchModalData, err?: Error) => void): void {
|
||||
setSubmitCb(submitCallback: (res: IdSearchModalData, err?: Error) => void): void {
|
||||
this.submitCallback = submitCallback;
|
||||
}
|
||||
|
||||
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||
setCloseCb(closeCallback: (err?: Error) => void): void {
|
||||
this.closeCallback = closeCallback;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ export class MediaDbPreviewModal extends Modal {
|
|||
this.markdownComponent = new Component();
|
||||
}
|
||||
|
||||
setSubmitCallback(submitCallback: (previewModalData: PreviewModalData) => void): void {
|
||||
setSubmitCb(submitCallback: (previewModalData: PreviewModalData) => void): void {
|
||||
this.submitCallback = submitCallback;
|
||||
}
|
||||
|
||||
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||
setCloseCb(closeCallback: (err?: Error) => void): void {
|
||||
this.closeCallback = closeCallback;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ export class MediaDbSearchModal extends Modal {
|
|||
this.isBusy = false;
|
||||
}
|
||||
|
||||
setSubmitCallback(submitCallback: (res: SearchModalData) => void): void {
|
||||
setSubmitCb(submitCallback: (res: SearchModalData) => void): void {
|
||||
this.submitCallback = submitCallback;
|
||||
}
|
||||
|
||||
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||
setCloseCb(closeCallback: (err?: Error) => void): void {
|
||||
this.closeCallback = closeCallback;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ export class MediaDbSearchResultModal extends SelectModal<MediaTypeModel> {
|
|||
this.sendCallback = false;
|
||||
}
|
||||
|
||||
setSubmitCallback(submitCallback: (res: SelectModalData) => void): void {
|
||||
setSubmitCb(submitCallback: (res: SelectModalData) => void): void {
|
||||
this.submitCallback = submitCallback;
|
||||
}
|
||||
|
||||
setCloseCallback(closeCallback: (err?: Error) => void): void {
|
||||
setCloseCb(closeCallback: (err?: Error) => void): void {
|
||||
this.closeCallback = closeCallback;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@ export class MediaDbSeasonSelectModal extends SelectModal<SeasonSelectModalEleme
|
|||
this.close();
|
||||
}
|
||||
|
||||
setSubmitCallback(cb: (selectedSeasons: SeasonSelectModalElement[]) => void): void {
|
||||
setSubmitCb(cb: (selectedSeasons: SeasonSelectModalElement[]) => void): void {
|
||||
this.submitCallback = cb;
|
||||
}
|
||||
|
||||
setCloseCallback(cb: (err?: Error) => void): void {
|
||||
setCloseCb(cb: (err?: Error) => void): void {
|
||||
this.closeCallback = cb;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export class SeasonModel extends MediaTypeModel {
|
|||
|
||||
migrateObject(this, obj, this);
|
||||
|
||||
if (!obj.hasOwnProperty('userData')) {
|
||||
if (!Object.hasOwn(obj, 'userData')) {
|
||||
migrateObject(this.userData, obj, this.userData);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,14 +124,16 @@ export class PropertyMapping {
|
|||
if (!this.property || !containsOnlyLettersAndUnderscores(this.property)) {
|
||||
return {
|
||||
res: false,
|
||||
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": property may not be empty and only contain letters and underscores.`),
|
||||
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": property may not be empty and may only contain letters and underscores.`),
|
||||
};
|
||||
}
|
||||
|
||||
if (!this.newProperty || !containsOnlyLettersAndUnderscores(this.newProperty)) {
|
||||
return {
|
||||
res: false,
|
||||
err: new PropertyMappingValidationError(`Error in property mapping "${this.toString()}": new property may not be empty and only contain letters and underscores.`),
|
||||
err: new PropertyMappingValidationError(
|
||||
`Error in property mapping "${this.toString()}": new property may not be empty and may only contain letters and underscores.`,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { PropertyMappingModel, PropertyMappingOption, propertyMappingOptions } from './PropertyMapping';
|
||||
import { capitalizeFirstLetter } from '../utils/Utils';
|
||||
import Icon from './Icon.svelte';
|
||||
|
||||
|
||||
interface Props {
|
||||
model: PropertyMappingModel;
|
||||
save: (model: PropertyMappingModel) => void;
|
||||
|
|
@ -10,70 +10,39 @@
|
|||
|
||||
let { model, save }: Props = $props();
|
||||
|
||||
let unsavedChanges = $state(false);
|
||||
// svelte-ignore state_referenced_locally
|
||||
|
||||
let validationResult: { res: boolean; err?: Error } | undefined = $derived(model.validate());
|
||||
|
||||
function onModelUpdate() {
|
||||
unsavedChanges = true;
|
||||
model = model.copy();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="media-db-plugin-property-mappings-model-container">
|
||||
<div class="setting-item-name">{capitalizeFirstLetter(model.type)}</div>
|
||||
<div class="media-db-plugin-property-mappings-model-header">
|
||||
<div class="setting-item-name">{capitalizeFirstLetter(model.type)}</div>
|
||||
|
||||
<table class="media-db-plugin-property-mappings-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-property">Property</th>
|
||||
<th class="col-mapping">Mapping</th>
|
||||
<th class="col-new-name">New name</th>
|
||||
<th class="col-wikilink">Wikilink</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each model.properties as property}
|
||||
<tr>
|
||||
<td class="col-property">
|
||||
<code>{property.property}</code>
|
||||
</td>
|
||||
<div class="media-db-plugin-property-mappings-model-actions">
|
||||
{#if unsavedChanges}
|
||||
<div class="media-db-plugin-property-mapping-unsaved-changes">Unsaved changes</div>
|
||||
{/if}
|
||||
|
||||
{#if property.locked}
|
||||
<td class="col-locked" colspan="3">
|
||||
<div class="media-db-plugin-property-binding-text">property cannot be remapped</div>
|
||||
</td>
|
||||
{:else}
|
||||
<td class="col-mapping">
|
||||
<select
|
||||
class="dropdown"
|
||||
bind:value={property.mapping}
|
||||
onchange={() => {
|
||||
model = model.copy();
|
||||
}}
|
||||
>
|
||||
{#each propertyMappingOptions as remappingOption}
|
||||
<option value={remappingOption}>
|
||||
{remappingOption}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="col-new-name">
|
||||
{#if property.mapping === PropertyMappingOption.Map}
|
||||
<div class="media-db-plugin-property-mapping-to">
|
||||
<Icon iconName="arrow-right" />
|
||||
<input class="media-db-plugin-property-mapping-input" type="text" spellcheck="false" bind:value={property.newProperty} />
|
||||
</div>
|
||||
{:else}
|
||||
<span class="media-db-plugin-property-mapping-to-disabled">—</span>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
<td class="col-wikilink">
|
||||
<label class="media-db-plugin-property-mapping-wikilink-label" title="Convert value to wikilink ([[value]])">
|
||||
<input type="checkbox" bind:checked={property.wikilink} />
|
||||
</label>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
<button
|
||||
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
|
||||
onclick={() => {
|
||||
if (model.validate().res) {
|
||||
save(model);
|
||||
unsavedChanges = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !validationResult?.res}
|
||||
<div class="media-db-plugin-property-mapping-validation">
|
||||
|
|
@ -81,33 +50,169 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="media-db-plugin-property-mappings-save-button {validationResult?.res ? 'mod-cta' : 'mod-muted'}"
|
||||
onclick={() => {
|
||||
if (model.validate().res) save(model);
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<div class="media-db-plugin-property-mappings-table-container">
|
||||
<table class="media-db-plugin-property-mappings-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-property">Property</th>
|
||||
<th class="col-mapping">Mapping</th>
|
||||
<th class="col-new-name">New name</th>
|
||||
<th class="col-wikilink">Wikilink</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each model.properties as property}
|
||||
<tr>
|
||||
<td class="col-property">
|
||||
<code>{property.property}</code>
|
||||
</td>
|
||||
|
||||
{#if property.locked}
|
||||
<td class="col-locked" colspan="3">
|
||||
<div class="media-db-plugin-property-binding-text">property cannot be remapped</div>
|
||||
</td>
|
||||
{:else}
|
||||
<td class="col-mapping">
|
||||
<select class="dropdown" bind:value={property.mapping} onchange={() => onModelUpdate()}>
|
||||
{#each propertyMappingOptions as remappingOption}
|
||||
<option value={remappingOption}>
|
||||
{remappingOption}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="col-new-name">
|
||||
{#if property.mapping === PropertyMappingOption.Map}
|
||||
<div class="media-db-plugin-property-mapping-to">
|
||||
<Icon iconName="arrow-right" />
|
||||
<input
|
||||
class="media-db-plugin-property-mapping-input"
|
||||
type="text"
|
||||
spellcheck="false"
|
||||
bind:value={property.newProperty}
|
||||
onchange={() => onModelUpdate()}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<span class="media-db-plugin-property-mapping-to-disabled">—</span>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
<td class="col-wikilink">
|
||||
<label class="media-db-plugin-property-mapping-wikilink-label" title="Convert value to wikilink ([[value]])">
|
||||
<input type="checkbox" bind:checked={property.wikilink} onchange={() => onModelUpdate()} />
|
||||
</label>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Container */
|
||||
.media-db-plugin-property-mappings-model-container {
|
||||
margin-bottom: var(--size-4-8);
|
||||
}
|
||||
|
||||
/* Header and actions */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Validation error */
|
||||
.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);
|
||||
}
|
||||
|
||||
/* Table container */
|
||||
.media-db-plugin-property-mappings-table-container {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mappings-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
table-layout: fixed; /* prevent overflow from wide cells */
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
/* remove excessive left padding and keep within container */
|
||||
.media-db-plugin-property-mappings-table th,
|
||||
.media-db-plugin-property-mappings-table td {
|
||||
padding: 2px 4px;
|
||||
/* Table header */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Table body */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* column widths */
|
||||
.media-db-plugin-property-mappings-table tbody tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Column widths */
|
||||
.col-property {
|
||||
width: 25%;
|
||||
white-space: nowrap;
|
||||
|
|
@ -126,36 +231,66 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
/* ensure inner controls don't push table wider than container */
|
||||
.media-db-plugin-property-mapping-to {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
.col-locked {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-input,
|
||||
/* Property name styling */
|
||||
.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);
|
||||
}
|
||||
|
||||
/* Locked property text */
|
||||
.media-db-plugin-property-binding-text {
|
||||
color: var(--text-muted);
|
||||
font-size: var(--font-ui-small);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Dropdown select - use Obsidian defaults */
|
||||
.media-db-plugin-property-mappings-table select.dropdown {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-wikilink-label {
|
||||
display: inline-flex;
|
||||
/* Remap input */
|
||||
.media-db-plugin-property-mapping-to {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.95em;
|
||||
cursor: pointer;
|
||||
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-muted);
|
||||
color: var(--text-faint);
|
||||
font-size: var(--font-ui-medium);
|
||||
}
|
||||
|
||||
/* avoid extra left indentation from <pre> */
|
||||
.media-db-plugin-property-mappings-table code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
/* Wikilink checkbox */
|
||||
.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);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -17,12 +17,6 @@
|
|||
|
||||
<!--
|
||||
<pre>{JSON.stringify(models, null, 4)}</pre>
|
||||
|
||||
{ #each ICON_LIST as icon }
|
||||
<p>
|
||||
{icon} <Icon iconName="{icon}"/>
|
||||
</p>
|
||||
{/each}
|
||||
-->
|
||||
</div>
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,24 +7,25 @@ import { GameModel } from '../models/GameModel';
|
|||
import type { MediaTypeModel } from '../models/MediaTypeModel';
|
||||
import { MovieModel } from '../models/MovieModel';
|
||||
import { MusicReleaseModel } from '../models/MusicReleaseModel';
|
||||
import { SeriesModel } from '../models/SeriesModel';
|
||||
import { SeasonModel } from '../models/SeasonModel';
|
||||
import { SeriesModel } from '../models/SeriesModel';
|
||||
import { WikiModel } from '../models/WikiModel';
|
||||
import type { MediaDbPluginSettings } from '../settings/Settings';
|
||||
import { ILLEGAL_FILENAME_CHARACTERS } from './IllegalFilenameCharactersList';
|
||||
import { MediaType } from './MediaType';
|
||||
import { replaceTags } from './Utils';
|
||||
|
||||
// All media types in alphabetical order
|
||||
export const MEDIA_TYPES: MediaType[] = [
|
||||
MediaType.Movie,
|
||||
MediaType.Series,
|
||||
MediaType.Season,
|
||||
MediaType.ComicManga,
|
||||
MediaType.Game,
|
||||
MediaType.Wiki,
|
||||
MediaType.MusicRelease,
|
||||
MediaType.BoardGame,
|
||||
MediaType.Book,
|
||||
MediaType.ComicManga,
|
||||
MediaType.Game,
|
||||
MediaType.Movie,
|
||||
MediaType.MusicRelease,
|
||||
MediaType.Series,
|
||||
MediaType.Season,
|
||||
MediaType.Wiki,
|
||||
];
|
||||
|
||||
export class MediaTypeManager {
|
||||
|
|
|
|||
|
|
@ -236,8 +236,8 @@ export class ModalHelper {
|
|||
async createSearchModal(searchModalOptions: SearchModalOptions): Promise<{ searchModalResult: SearchModalResult; searchModal: MediaDbSearchModal }> {
|
||||
const modal = new MediaDbSearchModal(this.plugin, searchModalOptions);
|
||||
const res: SearchModalResult = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCallback(err => {
|
||||
modal.setSubmitCb(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||
}
|
||||
|
|
@ -302,8 +302,8 @@ export class ModalHelper {
|
|||
): Promise<{ advancedSearchModalResult: AdvancedSearchModalResult; advancedSearchModal: MediaDbAdvancedSearchModal }> {
|
||||
const modal = new MediaDbAdvancedSearchModal(this.plugin, advancedSearchModalOptions);
|
||||
const res: AdvancedSearchModalResult = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCallback(err => {
|
||||
modal.setSubmitCb(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||
}
|
||||
|
|
@ -366,8 +366,8 @@ export class ModalHelper {
|
|||
async createIdSearchModal(idSearchModalOptions: IdSearchModalOptions): Promise<{ idSearchModalResult: IdSearchModalResult; idSearchModal: MediaDbIdSearchModal }> {
|
||||
const modal = new MediaDbIdSearchModal(this.plugin, idSearchModalOptions);
|
||||
const res: IdSearchModalResult = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCallback(err => {
|
||||
modal.setSubmitCb(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||
}
|
||||
|
|
@ -430,9 +430,9 @@ export class ModalHelper {
|
|||
async createSelectModal(selectModalOptions: SelectModalOptions): Promise<{ selectModalResult: SelectModalResult; selectModal: MediaDbSearchResultModal }> {
|
||||
const modal = new MediaDbSearchResultModal(this.plugin, selectModalOptions);
|
||||
const res: SelectModalResult = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setSubmitCb(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setSkipCallback(() => resolve({ code: ModalResultCode.SKIP }));
|
||||
modal.setCloseCallback(err => {
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||
}
|
||||
|
|
@ -493,8 +493,8 @@ export class ModalHelper {
|
|||
//todo: handle attachFile for existing files
|
||||
const modal = new MediaDbPreviewModal(this.plugin, previewModalOptions);
|
||||
const res: PreviewModalResult = await new Promise(resolve => {
|
||||
modal.setSubmitCallback(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCallback(err => {
|
||||
modal.setSubmitCb(res => resolve({ code: ModalResultCode.SUCCESS, data: res }));
|
||||
modal.setCloseCb(err => {
|
||||
if (err) {
|
||||
resolve({ code: ModalResultCode.ERROR, error: err });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import type { App } from 'obsidian';
|
||||
import type { SeasonSelectModalElement } from '../modals/MediaDbSeasonSelectModal';
|
||||
import { MediaDbSeasonSelectModal } from '../modals/MediaDbSeasonSelectModal';
|
||||
|
||||
export async function openSeasonSelectModal(app: App, plugin: any, seasons: SeasonSelectModalElement[]): Promise<SeasonSelectModalElement[] | undefined> {
|
||||
return new Promise(resolve => {
|
||||
const modal = new MediaDbSeasonSelectModal(plugin, seasons, true);
|
||||
modal.setSubmitCallback(selected => {
|
||||
resolve(selected);
|
||||
});
|
||||
modal.setCloseCallback(() => {
|
||||
resolve(undefined);
|
||||
});
|
||||
modal.open();
|
||||
});
|
||||
}
|
||||
51
styles.css
51
styles.css
|
|
@ -64,55 +64,6 @@ small.media-db-plugin-list-text {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* region property mappings */
|
||||
.media-db-plugin-property-mappings-model-container {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mappings-container {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element-property-name-wrapper {
|
||||
min-width: 160px;
|
||||
background: var(--background-modifier-form-field);
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-element-property-name {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mappings-save-button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-to {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.media-db-plugin-property-mapping-validation {
|
||||
color: var(--text-error);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.media-db-plugin-button:focus {
|
||||
/*outline: 1px solid white;*/
|
||||
}
|
||||
|
|
@ -122,5 +73,3 @@ small.media-db-plugin-list-text {
|
|||
border: var(--modal-border-width) solid var(--modal-border-color);
|
||||
padding: var(--size-4-4);
|
||||
}
|
||||
|
||||
/* endregion */
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
const targetVersion = process.env.npm_package_version;
|
||||
|
||||
// read minAppVersion from manifest.json and bump version to target version
|
||||
let manifest = JSON.parse(readFileSync('manifest.json', 'utf8'));
|
||||
const { minAppVersion } = manifest;
|
||||
manifest.version = targetVersion;
|
||||
writeFileSync('manifest.json', JSON.stringify(manifest, null, '\t'));
|
||||
|
||||
// update versions.json with target version and minAppVersion from manifest.json
|
||||
let versions = JSON.parse(readFileSync('versions.json', 'utf8'));
|
||||
versions[targetVersion] = minAppVersion;
|
||||
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
|
||||
Loading…
Add table
Add a link
Reference in a new issue