diff --git a/packages/web/src/lib/icons/mod.ts b/packages/web/src/lib/icons/mod.ts index ba42d401..9a6805f4 100644 --- a/packages/web/src/lib/icons/mod.ts +++ b/packages/web/src/lib/icons/mod.ts @@ -20,3 +20,7 @@ export {default as ArrowTopRightOnSquare} from './svgs/arrow-top-right-on-square export {default as MagnifyingGlassPlus} from './svgs/magnifying-glass-plus.svg?raw' export {default as Trash} from './svgs/trash.svg?raw' export {default as TrashOutline} from './svgs/outline/trash.svg?raw' +export {default as Eye} from './svgs/outline/eye.svg?raw' +export {default as EyeSlash} from './svgs/outline/eye-slash.svg?raw' +export {default as TagIcon} from './svgs/outline/tag.svg?raw' +export {default as Calendar} from './svgs/outline/calendar.svg?raw' diff --git a/packages/web/src/lib/icons/svgs/outline/calendar.svg b/packages/web/src/lib/icons/svgs/outline/calendar.svg new file mode 100644 index 00000000..17fc992b --- /dev/null +++ b/packages/web/src/lib/icons/svgs/outline/calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/src/lib/icons/svgs/outline/eye-slash.svg b/packages/web/src/lib/icons/svgs/outline/eye-slash.svg new file mode 100644 index 00000000..dcfb31ee --- /dev/null +++ b/packages/web/src/lib/icons/svgs/outline/eye-slash.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/web/src/lib/icons/svgs/outline/eye.svg b/packages/web/src/lib/icons/svgs/outline/eye.svg new file mode 100644 index 00000000..40fecb7d --- /dev/null +++ b/packages/web/src/lib/icons/svgs/outline/eye.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/web/src/lib/icons/svgs/outline/tag.svg b/packages/web/src/lib/icons/svgs/outline/tag.svg new file mode 100644 index 00000000..96dbd320 --- /dev/null +++ b/packages/web/src/lib/icons/svgs/outline/tag.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/web/src/lib/runes/settings_rune.svelte.ts b/packages/web/src/lib/runes/settings_rune.svelte.ts index 4e335605..16557802 100644 --- a/packages/web/src/lib/runes/settings_rune.svelte.ts +++ b/packages/web/src/lib/runes/settings_rune.svelte.ts @@ -5,6 +5,7 @@ import type { BaseController } from '$lib/base_controller.ts' interface MutatableSettings { 'ui.media_list.thumbnail_size': Config['web']['ui_defaults']['media_list']['thumbnail_size'] 'ui.media_list.thumbnail_shape': Config['web']['ui_defaults']['media_list']['thumbnail_shape'] + 'ui.media_list.info_tiles.enabled': Config['web']['ui_defaults']['media_list']['info_tiles']['enabled'] 'ui.search.advanced_filters.hide': Config['web']['ui_defaults']['search']['advanced_filters']['hide'] 'ui.sidebar.hide': Config['web']['ui_defaults']['sidebar']['hide'] } @@ -35,6 +36,10 @@ export class SettingsRune extends Rune { this.ui.media_list.thumbnail_shape = value break } + case 'ui.media_list.info_tiles.enabled': { + this.ui.media_list.info_tiles.enabled = value + break + } case 'ui.search.advanced_filters.hide': { this.ui.search.advanced_filters.hide = value break @@ -65,6 +70,9 @@ export class SettingsRune extends Rune { case 'ui.media_list.thumbnail_shape': { return this.ui.media_list.thumbnail_shape } + case 'ui.media_list.info_tiles.enabled': { + return this.ui.media_list.info_tiles.enabled + } case 'ui.search.advanced_filters.hide': { return this.ui.search.advanced_filters.hide } diff --git a/packages/web/src/lib/server/config.ts b/packages/web/src/lib/server/config.ts index 1e2ce36c..74ac40e4 100644 --- a/packages/web/src/lib/server/config.ts +++ b/packages/web/src/lib/server/config.ts @@ -5,7 +5,15 @@ import * as forager from '@forager/core' const LogLevel = z.enum(['SILENT', 'ERROR', 'WARN', 'INFO', 'DEBUG']) -const MediaTileInfoTableTypes = z.enum(['tag_group', 'sort_top']) +const MediaTileInfoTableTypes = z.enum([ + 'tag_group', + 'sort_top', + 'tag_count', + 'unread', + 'stars', + 'datetime', + 'series_count', +]) const Keybind = (default_keybind: string) => z.union([z.string(), z.string().array()]).default(default_keybind).transform(keybind => { if (Array.isArray(keybind)) return keybind @@ -43,6 +51,7 @@ export const PackagesConfig = z.object({ thumbnail_shape: z.enum(['square', 'original']).prefault('original'), info_tiles: z.object({ + enabled: z.boolean().default(true), tag_group: z.object({ enabled: z.boolean().default(true), order: z.number().optional(), // TBD if needed. We may be able to get away with yaml ordering if the serialization is consistent @@ -51,6 +60,26 @@ export const PackagesConfig = z.object({ enabled: z.boolean().default(true), order: z.number().optional(), // TBD if needed. We may be able to get away with yaml ordering if the serialization is consistent }).prefault({}), + tag_count: z.object({ + enabled: z.boolean().default(true), + order: z.number().optional(), + }).prefault({}), + unread: z.object({ + enabled: z.boolean().default(true), + order: z.number().optional(), + }).prefault({}), + stars: z.object({ + enabled: z.boolean().default(true), + order: z.number().optional(), + }).prefault({}), + datetime: z.object({ + enabled: z.boolean().default(true), + order: z.number().optional(), + }).prefault({}), + series_count: z.object({ + enabled: z.boolean().default(true), + order: z.number().optional(), + }).prefault({}), }).prefault({}), }).strict().prefault({}), diff --git a/packages/web/src/routes/browse/components/Footer.svelte b/packages/web/src/routes/browse/components/Footer.svelte index 3c6445f2..ab1ea137 100644 --- a/packages/web/src/routes/browse/components/Footer.svelte +++ b/packages/web/src/routes/browse/components/Footer.svelte @@ -71,7 +71,7 @@ {runes.settings.ui.media_list.thumbnail_size}px -
+
+
diff --git a/packages/web/src/routes/browse/components/MediaTileInfoTable.svelte b/packages/web/src/routes/browse/components/MediaTileInfoTable.svelte index 0df57619..b6947463 100644 --- a/packages/web/src/routes/browse/components/MediaTileInfoTable.svelte +++ b/packages/web/src/routes/browse/components/MediaTileInfoTable.svelte @@ -5,13 +5,19 @@ import Icon from '$lib/components/Icon.svelte' import SearchLink from './SearchLink.svelte' - interface InfoEntry { + interface StarsValue { + type: 'stars' + value: number + max?: number + } + + export interface InfoEntry { name: string icon: string title?: string - stat?: object - queryparams?: string - value: object + stat?: unknown + queryparams?: Partial + value: string | number | StarsValue enabled: boolean order: number } @@ -25,15 +31,15 @@ .filter(entry => entry.enabled) .sort((a, b) => a.order - b.order) ) - const {queryparams, settings, media_selections, media_list} = controller.runes const icon_size = 14 const icon_color = theme.colors.green[200] + const star_filled_color = theme.colors.yellow?.[300] ?? theme.colors.green[200] + const star_empty_color = theme.colors.gray[600] - const row_colors = [ - theme.colors.gray[800], - theme.colors.gray[700], - ] + function is_stars(value: InfoEntry['value']): value is StarsValue { + return typeof value === 'object' && value !== null && (value as StarsValue).type === 'stars' + } @@ -41,25 +47,36 @@ {#each entries as entry, index} - - - {#if entry.queryparams} + + {#if is_stars(entry.value)} + {@const max = entry.value.max ?? 5} + + {#each Array(max) as _, i} + + {/each} + + {:else if entry.queryparams} {entry.value} {:else} - {entry.value} + {entry.value} {/if} - {entry.stat} + {entry.stat ?? ''} {/each} diff --git a/packages/web/src/routes/browse/components/SearchResults.svelte b/packages/web/src/routes/browse/components/SearchResults.svelte index c4844fa3..6d81bb95 100644 --- a/packages/web/src/routes/browse/components/SearchResults.svelte +++ b/packages/web/src/routes/browse/components/SearchResults.svelte @@ -33,7 +33,7 @@ return `${hours.toFixed(2)}h` } - function format_human_readable_datetime(date_str: string) { + function format_human_readable_datetime(date_str: string | Date) { const date = new Date(date_str) return datetime.format(date, 'yyyy-MM-dd') } @@ -48,6 +48,19 @@ ? controller.runes.queryparams.current.sort : 'created_at' } + + function pick_datetime(media_reference: {source_created_at?: string | Date | null, created_at: string | Date}) { + if (media_reference.source_created_at) { + return { + field: 'source_created_at' as const, + value: media_reference.source_created_at, + } + } + return { + field: 'created_at' as const, + value: media_reference.created_at, + } + }