diff options
Diffstat (limited to 'src')
18 files changed, 394 insertions, 221 deletions
diff --git a/src/content/dependencies/generateCoverArtworkOriginDetails.js b/src/content/dependencies/generateCoverArtworkOriginDetails.js index 3eb7c664..3908414f 100644 --- a/src/content/dependencies/generateCoverArtworkOriginDetails.js +++ b/src/content/dependencies/generateCoverArtworkOriginDetails.js @@ -15,8 +15,8 @@ export default { artwork.thing.constructor[Thing.referenceType], attachedArtistContribs: - (!artwork.isMainArtwork && artwork.mainArtwork && artwork.attachAbove - ? artwork.mainArtwork.artistContribs + (artwork.attachedArtwork + ? artwork.attachedArtwork.artistContribs : null) }), @@ -58,6 +58,11 @@ export default { {class: 'origin-details'}, (() => { + relations.datetimestamp?.setSlots({ + style: 'year', + tooltip: true, + }); + const artworkBy = language.encapsulate(capsule, 'artworkBy', workingCapsule => { const workingOptions = {}; @@ -69,11 +74,7 @@ export default { if (relations.datetimestamp) { workingCapsule += '.withYear'; - workingOptions.year = - relations.datetimestamp.slots({ - style: 'year', - tooltip: true, - }); + workingOptions.year = relations.datetimestamp; } return relations.credit.slots({ @@ -111,15 +112,38 @@ export default { workingOptions.label = data.label; } + if (html.isBlank(artworkBy) && relations.datetimestamp) { + workingCapsule += '.withYear'; + workingOptions.year = relations.datetimestamp; + } + return language.$(workingCapsule, workingOptions); }); const label = html.isBlank(artworkBy) && html.isBlank(source) && - language.$(capsule, 'customLabelAlone', { - [language.onlyIfOptions]: ['label'], - label: data.label, + language.encapsulate(capsule, 'customLabel', workingCapsule => { + const workingOptions = { + [language.onlyIfOptions]: ['label'], + label: data.label, + }; + + if (relations.datetimestamp) { + workingCapsule += '.withYear'; + workingOptions.year = relations.datetimestamp; + } + + return language.$(workingCapsule, workingOptions); + }); + + const year = + html.isBlank(artworkBy) && + html.isBlank(source) && + html.isBlank(label) && + language.$(capsule, 'year', { + [language.onlyIfOptions]: ['year'], + year: relations.datetimestamp, }); return [ @@ -127,6 +151,7 @@ export default { trackArtFromAlbum, source, label, + year, ]; })())), }; diff --git a/src/content/dependencies/generateGroupInfoPageAlbumsListItem.js b/src/content/dependencies/generateGroupInfoPageAlbumsListItem.js index 99e7e8ff..4680cb46 100644 --- a/src/content/dependencies/generateGroupInfoPageAlbumsListItem.js +++ b/src/content/dependencies/generateGroupInfoPageAlbumsListItem.js @@ -127,7 +127,8 @@ export default { workingCapsule += '.withArtists'; workingOptions.by = html.tag('span', {class: 'by'}, - html.metatag('chunkwrap', {split: ','}, + // TODO: This is obviously evil. + html.metatag('chunkwrap', {split: /,| (?=and)/}, html.resolve(artistCredit))); } diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js index 8dad97e0..89fefb23 100644 --- a/src/content/dependencies/generatePageLayout.js +++ b/src/content/dependencies/generatePageLayout.js @@ -268,12 +268,16 @@ export default { const maybeTemplate = apparentFirst(slots.artworkColumnContent); + if (!maybeTemplate) return null; + const maybeTemplateContent = html.resolve(maybeTemplate, {normalize: 'tag'}); const maybeCoverArtwork = apparentFirst(maybeTemplateContent); + if (!maybeCoverArtwork) return null; + if (maybeCoverArtwork.attributes.has('class', 'cover-artwork')) { return maybeTemplate; } else { diff --git a/src/content/dependencies/generateTrackListItem.js b/src/content/dependencies/generateTrackListItem.js index 887b6f03..3c850a18 100644 --- a/src/content/dependencies/generateTrackListItem.js +++ b/src/content/dependencies/generateTrackListItem.js @@ -97,7 +97,8 @@ export default { workingCapsule += '.withArtists'; workingOptions.by = html.tag('span', {class: 'by'}, - html.metatag('chunkwrap', {split: ','}, + // TODO: This is obviously evil. + html.metatag('chunkwrap', {split: /,| (?=and)/}, html.resolve(relations.credit))); } diff --git a/src/data/composite/control-flow/flipFilter.js b/src/data/composite/control-flow/flipFilter.js new file mode 100644 index 00000000..995bacad --- /dev/null +++ b/src/data/composite/control-flow/flipFilter.js @@ -0,0 +1,36 @@ +// Flips a filter, so that each true item becomes false, and vice versa. +// Overwrites the provided dependency. +// +// See also: +// - withAvailabilityFilter + +import {input, templateCompositeFrom} from '#composite'; + +export default templateCompositeFrom({ + annotation: `flipFilter`, + + inputs: { + filter: input({type: 'array'}), + }, + + outputs: ({ + [input.staticDependency('filter')]: filterDependency, + }) => [filterDependency ?? '#flippedFilter'], + + steps: () => [ + { + dependencies: [ + input('filter'), + input.staticDependency('filter'), + ], + + compute: (continuation, { + [input('filter')]: filter, + [input.staticDependency('filter')]: filterDependency, + }) => continuation({ + [filterDependency ?? '#flippedFilter']: + filter.map(item => !item), + }), + }, + ], +}); diff --git a/src/data/composite/control-flow/index.js b/src/data/composite/control-flow/index.js index 7e137a14..778dc66b 100644 --- a/src/data/composite/control-flow/index.js +++ b/src/data/composite/control-flow/index.js @@ -10,6 +10,7 @@ export {default as exposeDependency} from './exposeDependency.js'; export {default as exposeDependencyOrContinue} from './exposeDependencyOrContinue.js'; export {default as exposeUpdateValueOrContinue} from './exposeUpdateValueOrContinue.js'; export {default as exposeWhetherDependencyAvailable} from './exposeWhetherDependencyAvailable.js'; +export {default as flipFilter} from './flipFilter.js'; export {default as raiseOutputWithoutDependency} from './raiseOutputWithoutDependency.js'; export {default as raiseOutputWithoutUpdateValue} from './raiseOutputWithoutUpdateValue.js'; export {default as withAvailabilityFilter} from './withAvailabilityFilter.js'; diff --git a/src/data/composite/control-flow/withAvailabilityFilter.js b/src/data/composite/control-flow/withAvailabilityFilter.js index cfea998e..fd93af71 100644 --- a/src/data/composite/control-flow/withAvailabilityFilter.js +++ b/src/data/composite/control-flow/withAvailabilityFilter.js @@ -4,6 +4,7 @@ // Accepts the same mode options as withResultOfAvailabilityCheck. // // See also: +// - flipFilter // - withFilteredList // - withResultOfAvailabilityCheck // diff --git a/src/data/composite/data/withFilteredList.js b/src/data/composite/data/withFilteredList.js index 44c1661d..15ee3373 100644 --- a/src/data/composite/data/withFilteredList.js +++ b/src/data/composite/data/withFilteredList.js @@ -2,9 +2,6 @@ // corresponding items in a list. Items which correspond to a truthy value // are kept, and the rest are excluded from the output list. // -// If the flip option is set, only items corresponding with a *falsy* value in -// the filter are kept. -// // TODO: There should be two outputs - one for the items included according to // the filter, and one for the items excluded. // @@ -22,28 +19,19 @@ export default templateCompositeFrom({ inputs: { list: input({type: 'array'}), filter: input({type: 'array'}), - - flip: input({ - type: 'boolean', - defaultValue: false, - }), }, outputs: ['#filteredList'], steps: () => [ { - dependencies: [input('list'), input('filter'), input('flip')], + dependencies: [input('list'), input('filter')], compute: (continuation, { [input('list')]: list, [input('filter')]: filter, - [input('flip')]: flip, }) => continuation({ '#filteredList': - list.filter((_item, index) => - (flip - ? !filter[index] - : filter[index])), + list.filter((_item, index) => filter[index]), }), }, ], diff --git a/src/data/composite/data/withNearbyItemFromList.js b/src/data/composite/data/withNearbyItemFromList.js index 83a8cc21..5e165219 100644 --- a/src/data/composite/data/withNearbyItemFromList.js +++ b/src/data/composite/data/withNearbyItemFromList.js @@ -9,6 +9,10 @@ // - If the 'valuePastEdge' input is provided, that value will be output // instead of null. // +// - If the 'filter' input is provided, corresponding items will be skipped, +// and only (repeating `offset`) the next included in the filter will be +// returned. +// // Both the list and item must be provided. // // See also: @@ -16,7 +20,6 @@ // import {input, templateCompositeFrom} from '#composite'; -import {atOffset} from '#sugar'; import {raiseOutputWithoutDependency} from '#composite/control-flow'; @@ -28,9 +31,12 @@ export default templateCompositeFrom({ inputs: { list: input({acceptsNull: false, type: 'array'}), item: input({acceptsNull: false}), - offset: input({type: 'number'}), + wrap: input({type: 'boolean', defaultValue: false}), + valuePastEdge: input({defaultValue: null}), + + filter: input({defaultValue: null, type: 'array'}), }, outputs: ['#nearbyItem'], @@ -45,29 +51,55 @@ export default templateCompositeFrom({ dependency: '#index', mode: input.value('index'), - output: input.value({ - ['#nearbyItem']: - null, - }), + output: input.value({'#nearbyItem': null}), }), { dependencies: [ input('list'), input('offset'), + input('wrap'), + input('valuePastEdge'), + + input('filter'), + '#index', ], compute: (continuation, { [input('list')]: list, [input('offset')]: offset, + [input('wrap')]: wrap, + [input('valuePastEdge')]: valuePastEdge, + + [input('filter')]: filter, + ['#index']: index, - }) => continuation({ - ['#nearbyItem']: - atOffset(list, index, offset, {wrap}), - }), + }) => { + const startIndex = index; + + do { + index += offset; + + if (wrap) { + index = index % list.length; + } else if (index < 0) { + return continuation({'#nearbyItem': valuePastEdge}); + } else if (index >= list.length) { + return continuation({'#nearbyItem': valuePastEdge}); + } + + if (filter && !filter[index]) { + continue; + } + + return continuation({'#nearbyItem': list[index]}); + } while (index !== startIndex); + + return continuation({'#nearbyItem': null}); + }, }, ], }); diff --git a/src/data/composite/things/artwork/index.js b/src/data/composite/things/artwork/index.js index 5a592777..3693c10f 100644 --- a/src/data/composite/things/artwork/index.js +++ b/src/data/composite/things/artwork/index.js @@ -1,4 +1,5 @@ +export {default as withAttachedArtwork} from './withAttachedArtwork.js'; export {default as withContainingArtworkList} from './withContainingArtworkList.js'; -export {default as withContribsFromMainArtwork} from './withContribsFromMainArtwork.js'; +export {default as withContribsFromAttachedArtwork} from './withContribsFromAttachedArtwork.js'; export {default as withDate} from './withDate.js'; -export {default as withPropertyFromMainArtwork} from './withPropertyFromMainArtwork.js'; +export {default as withPropertyFromAttachedArtwork} from './withPropertyFromAttachedArtwork.js'; diff --git a/src/data/composite/things/artwork/withAttachedArtwork.js b/src/data/composite/things/artwork/withAttachedArtwork.js new file mode 100644 index 00000000..d7c0d87b --- /dev/null +++ b/src/data/composite/things/artwork/withAttachedArtwork.js @@ -0,0 +1,43 @@ +import {input, templateCompositeFrom} from '#composite'; + +import {flipFilter, raiseOutputWithoutDependency} + from '#composite/control-flow'; +import {withNearbyItemFromList, withPropertyFromList} from '#composite/data'; + +import withContainingArtworkList from './withContainingArtworkList.js'; + +export default templateCompositeFrom({ + annotaion: `withContribsFromMainArtwork`, + + outputs: ['#attachedArtwork'], + + steps: () => [ + raiseOutputWithoutDependency({ + dependency: 'attachAbove', + mode: input.value('falsy'), + output: input.value({'#attachedArtwork': null}), + }), + + withContainingArtworkList(), + + withPropertyFromList({ + list: '#containingArtworkList', + property: input.value('attachAbove'), + }), + + flipFilter({ + filter: '#containingArtworkList.attachAbove', + }).outputs({ + '#containingArtworkList.attachAbove': '#filterNotAttached', + }), + + withNearbyItemFromList({ + list: '#containingArtworkList', + item: input.myself(), + offset: input.value(-1), + filter: '#filterNotAttached', + }).outputs({ + '#nearbyItem': '#attachedArtwork', + }), + ], +}); diff --git a/src/data/composite/things/artwork/withContribsFromMainArtwork.js b/src/data/composite/things/artwork/withContribsFromAttachedArtwork.js index 25616ad6..36abb3fe 100644 --- a/src/data/composite/things/artwork/withContribsFromMainArtwork.js +++ b/src/data/composite/things/artwork/withContribsFromAttachedArtwork.js @@ -4,26 +4,25 @@ import {raiseOutputWithoutDependency} from '#composite/control-flow'; import {withPropertyFromObject} from '#composite/data'; import {withRecontextualizedContributionList} from '#composite/wiki-data'; -import withPropertyFromMainArtwork from './withPropertyFromMainArtwork.js'; +import withPropertyFromAttachedArtwork from './withPropertyFromAttachedArtwork.js'; export default templateCompositeFrom({ - annotaion: `withContribsFromMainArtwork`, + annotaion: `withContribsFromAttachedArtwork`, - outputs: ['#mainArtwork.artistContribs'], + outputs: ['#attachedArtwork.artistContribs'], steps: () => [ - withPropertyFromMainArtwork({ + withPropertyFromAttachedArtwork({ property: input.value('artistContribs'), - onlyIfAttached: input.value(true), }), raiseOutputWithoutDependency({ - dependency: '#mainArtwork.artistContribs', - output: input.value({'#mainArtwork.artistContribs': null}), + dependency: '#attachedArtwork.artistContribs', + output: input.value({'#attachedArtwork.artistContribs': null}), }), withRecontextualizedContributionList({ - list: '#mainArtwork.artistContribs', + list: '#attachedArtwork.artistContribs', }), ], }); diff --git a/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js b/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js new file mode 100644 index 00000000..a2f954b9 --- /dev/null +++ b/src/data/composite/things/artwork/withPropertyFromAttachedArtwork.js @@ -0,0 +1,65 @@ +import {input, templateCompositeFrom} from '#composite'; + +import {withResultOfAvailabilityCheck} from '#composite/control-flow'; +import {withPropertyFromObject} from '#composite/data'; + +import withAttachedArtwork from './withAttachedArtwork.js'; + +function getOutputName({ + [input.staticValue('property')]: property, +}) { + if (property) { + return `#attachedArtwork.${property}`; + } else { + return '#value'; + } +} + +export default templateCompositeFrom({ + annotation: `withPropertyFromAttachedArtwork`, + + inputs: { + property: input({type: 'string'}), + }, + + outputs: inputs => [getOutputName(inputs)], + + steps: () => [ + { + dependencies: [input.staticValue('property')], + compute: (continuation, inputs) => + continuation({'#output': getOutputName(inputs)}), + }, + + withAttachedArtwork(), + + withResultOfAvailabilityCheck({ + from: '#attachedArtwork', + }), + + { + dependencies: ['#availability', '#output'], + compute: (continuation, { + ['#availability']: availability, + ['#output']: output, + }) => + (availability + ? continuation() + : continuation.raiseOutput({[output]: null})), + }, + + withPropertyFromObject({ + object: '#attachedArtwork', + property: input('property'), + }), + + { + dependencies: ['#value', '#output'], + compute: (continuation, { + ['#value']: value, + ['#output']: output, + }) => + continuation.raiseOutput({[output]: value}), + }, + ], +}); diff --git a/src/data/composite/things/artwork/withPropertyFromMainArtwork.js b/src/data/composite/things/artwork/withPropertyFromMainArtwork.js deleted file mode 100644 index a0233119..00000000 --- a/src/data/composite/things/artwork/withPropertyFromMainArtwork.js +++ /dev/null @@ -1,100 +0,0 @@ -import {input, templateCompositeFrom} from '#composite'; - -import {withResultOfAvailabilityCheck} from '#composite/control-flow'; -import {withPropertyFromObject} from '#composite/data'; - -import withContainingArtworkList from './withContainingArtworkList.js'; - -function getOutputName({ - [input.staticValue('property')]: property, -}) { - if (property) { - return `#mainArtwork.${property}`; - } else { - return '#value'; - } -} - -export default templateCompositeFrom({ - annotation: `withPropertyFromMainArtwork`, - - inputs: { - property: input({type: 'string'}), - onlyIfAttached: input({type: 'boolean', defaultValue: false}), - }, - - outputs: inputs => [getOutputName(inputs)], - - steps: () => [ - { - dependencies: [input.staticValue('property')], - compute: (continuation, inputs) => - continuation({'#output': getOutputName(inputs)}), - }, - - { - dependencies: [input('onlyIfAttached'), 'attachAbove', '#output'], - compute: (continuation, { - [input('onlyIfAttached')]: onlyIfAttached, - ['attachAbove']: attachAbove, - ['#output']: output, - }) => - (onlyIfAttached && attachAbove - ? continuation() - : onlyIfAttached - ? continuation.raiseOutput({[output]: null}) - : continuation()), - }, - - withContainingArtworkList(), - - withResultOfAvailabilityCheck({ - from: '#containingArtworkList', - }), - - { - dependencies: ['#availability', '#output'], - compute: (continuation, { - ['#availability']: availability, - ['#output']: output, - }) => - (availability - ? continuation() - : continuation.raiseOutput({[output]: null})), - }, - - { - dependencies: ['#containingArtworkList'], - compute: (continuation, { - ['#containingArtworkList']: list, - }) => - continuation({'#mainArtwork': list[0]}), - }, - - { - dependencies: [input.myself(), '#mainArtwork', '#output'], - compute: (continuation, { - [input.myself()]: myself, - ['#mainArtwork']: mainArtwork, - ['#output']: output, - }) => - (myself === mainArtwork - ? continuation.raiseOutput({[output]: null}) - : continuation()), - }, - - withPropertyFromObject({ - object: '#mainArtwork', - property: input('property'), - }), - - { - dependencies: ['#value', '#output'], - compute: (continuation, { - ['#value']: value, - ['#output']: output, - }) => - continuation.raiseOutput({[output]: value}), - }, - ], -}); diff --git a/src/data/things/artwork.js b/src/data/things/artwork.js index 8c88dea7..3cdb07d0 100644 --- a/src/data/things/artwork.js +++ b/src/data/things/artwork.js @@ -54,9 +54,10 @@ import { } from '#composite/wiki-properties'; import { + withAttachedArtwork, withContainingArtworkList, - withContribsFromMainArtwork, - withPropertyFromMainArtwork, + withContribsFromAttachedArtwork, + withPropertyFromAttachedArtwork, withDate, } from '#composite/things/artwork'; @@ -178,10 +179,10 @@ export class Artwork extends Thing { mode: input.value('empty'), }), - withContribsFromMainArtwork(), + withContribsFromAttachedArtwork(), exposeDependencyOrContinue({ - dependency: '#mainArtwork.artistContribs', + dependency: '#attachedArtwork.artistContribs', }), exitWithoutDependency({ @@ -222,13 +223,12 @@ export class Artwork extends Thing { mode: input.value('empty'), }), - withPropertyFromMainArtwork({ + withPropertyFromAttachedArtwork({ property: input.value('artTags'), - onlyIfAttached: input.value(true), }), exposeDependencyOrContinue({ - dependency: '#mainArtwork.artTags', + dependency: '#attachedArtwork.artTags', }), exitWithoutDependency({ @@ -360,36 +360,17 @@ export class Artwork extends Thing { }, ], - siblingArtworks: [ - withContainingArtworkList(), - - exitWithoutDependency({ - dependency: '#containingArtworkList', - value: input.value(null), - }), + attachedArtwork: [ + withAttachedArtwork(), - withIndexInList({ - list: '#containingArtworkList', - item: input.myself(), - }), - - exitWithoutDependency({ - dependency: '#index', - mode: input.value('index'), - value: input.value(null), + exposeDependency({ + dependency: '#attachedArtwork', }), - - { - dependencies: ['#containingArtworkList', '#index'], - compute: ({ - ['#containingArtworkList']: list, - ['#index']: index, - }) => [ - ...list.slice(0, index), - ...list.slice(index + 1), - ], - }, ], + + attachingArtworks: reverseReferenceList({ + reverse: soupyReverse.input('artworksWhichAttach'), + }), }); static [Thing.yamlDocumentSpec] = { @@ -448,6 +429,18 @@ export class Artwork extends Thing { date: ({artwork}) => artwork.date, }, + artworksWhichAttach: { + bindTo: 'artworkData', + + referencing: referencingArtwork => + (referencingArtwork.attachAbove + ? [referencingArtwork] + : []), + + referenced: referencingArtwork => + [referencingArtwork.attachedArtwork], + }, + artworksWhichFeature: { bindTo: 'artworkData', diff --git a/src/html.js b/src/html.js index 0fe424df..9e4c39ab 100644 --- a/src/html.js +++ b/src/html.js @@ -512,6 +512,10 @@ export class Tag { } } + #getAttributeRaw(attribute) { + return this.attributes.get(attribute); + } + set onlyIfContent(value) { this.#setAttributeFlag(onlyIfContent, value); } @@ -662,7 +666,7 @@ export class Tag { const chunkwrapSplitter = (this.chunkwrap - ? this.#getAttributeString('split') + ? this.#getAttributeRaw('split') : null); let seenChunkwrapSplitter = @@ -727,7 +731,7 @@ export class Tag { const chunkwrapChunks = (typeof nonTemplateItem === 'string' && chunkwrapSplitter - ? itemContent.split(chunkwrapSplitter) + ? Array.from(getChunkwrapChunks(itemContent, chunkwrapSplitter)) : null); const itemIncludesChunkwrapSplit = @@ -773,7 +777,7 @@ export class Tag { appendItemContent: { if (itemIncludesChunkwrapSplit) { - for (const [index, chunk] of chunkwrapChunks.entries()) { + for (const [index, {chunk, following}] of chunkwrapChunks.entries()) { if (index === 0) { // The first chunk isn't actually a chunk all on its own, it's // text that should be appended to the previous chunk. We will @@ -781,12 +785,27 @@ export class Tag { // the next chunk. content += chunk; } else { - const whitespace = chunk.match(/^\s+/) ?? ''; - content += chunkwrapSplitter; + const followingWhitespace = following.match(/\s+$/) ?? ''; + const chunkWhitespace = chunk.match(/^\s+/) ?? ''; + + if (followingWhitespace) { + content += following.slice(0, -followingWhitespace.length); + } else { + content += following; + } + content += '</span>'; - content += whitespace; + + content += followingWhitespace; + content += chunkWhitespace; + content += '<span class="chunkwrap">'; - content += chunk.slice(whitespace.length); + + if (chunkWhitespace) { + content += chunk.slice(chunkWhitespace.length); + } else { + content += chunk; + } } } @@ -1009,6 +1028,49 @@ export class Tag { } } +export function* getChunkwrapChunks(content, splitter) { + const splitString = + (typeof splitter === 'string' + ? splitter + : null); + + if (splitString) { + let following = ''; + for (const chunk of content.split(splitString)) { + yield {chunk, following}; + following = splitString; + } + + return; + } + + const splitRegExp = + (splitter instanceof RegExp + ? new RegExp( + splitter.source, + (splitter.flags.includes('g') + ? splitter.flags + : splitter.flags + 'g')) + : null); + + if (splitRegExp) { + let following = ''; + let prevIndex = 0; + for (const match of content.matchAll(splitRegExp)) { + const chunk = content.slice(prevIndex, match.index); + yield {chunk, following}; + + following = match[0]; + prevIndex = match.index + match[0].length; + } + + const chunk = content.slice(prevIndex); + yield {chunk, following}; + + return; + } +} + export function attributes(attributes) { return new Attributes(attributes); } @@ -1254,6 +1316,9 @@ export class Attributes { return value.some(Boolean); } else if (value === null) { return false; + } else if (value instanceof RegExp) { + // Oooooooo. + return true; } else { // Other objects are an error. break; @@ -1285,13 +1350,16 @@ export class Attributes { case 'number': return value.toString(); - // If it's a kept object, it's an array. case 'object': { - const joiner = - (descriptor?.arraylike && descriptor?.join) - ?? ' '; + if (Array.isArray(value)) { + const joiner = + (descriptor?.arraylike && descriptor?.join) + ?? ' '; - return value.filter(Boolean).join(joiner); + return value.filter(Boolean).join(joiner); + } else { + return value; + } } default: @@ -1963,6 +2031,8 @@ export const isAttributeValue = anyOf( isString, isNumber, isBoolean, isArray, isTag, isTemplate, + // Evil. Ooooo + validateInstanceOf(RegExp), validateArrayItems(item => isAttributeValue(item))); export const isAttributesAdditionPair = pair => { diff --git a/src/static/css/site.css b/src/static/css/site.css index b087582a..ca05a42e 100644 --- a/src/static/css/site.css +++ b/src/static/css/site.css @@ -161,10 +161,9 @@ body::before, .wallpaper-part { } .sidebar-column { - flex: 1 1 20%; + flex: 1 1 35%; min-width: 150px; max-width: 250px; - flex-basis: 250px; align-self: flex-start; } @@ -1632,6 +1631,10 @@ p.image-details.origin-details { margin-bottom: 2px; } +.cover-artwork-joiner { + z-index: -2; +} + .cover-artwork-joiner::after { content: ""; display: block; @@ -1669,35 +1672,26 @@ p.content-heading:has(+ .commentary-entry-heading.dated) { } .commentary-entry-heading { - display: flex; - flex-direction: row; - margin-left: 15px; - padding-left: 5px; - max-width: 625px; + padding-left: calc(5px + 1.25ch); + text-indent: -1.25ch; + margin-right: min(calc(8vw - 35px), 45px); padding-bottom: 0.2em; border-bottom: 1px solid var(--dim-color); } -.commentary-entry-heading-text { - flex-grow: 1; - padding-left: 1.25ch; - text-indent: -1.25ch; -} - .commentary-entry-accent { font-style: oblique; } .commentary-entry-heading .commentary-date { - flex-shrink: 0; - - margin-left: 0.75ch; - align-self: flex-end; + display: inline-block; + text-indent: 0; +} - padding-left: 0.5ch; - padding-right: 0.25ch; +.commentary-entry-heading.dated .commentary-entry-heading-text { + margin-right: 0.75ch; } .commentary-entry-heading .hoverable { @@ -1737,7 +1731,6 @@ p.content-heading:has(+ .commentary-entry-heading.dated) { .lyrics-entry { padding-left: 40px; - max-width: 600px; } .js-hide, @@ -1957,7 +1950,6 @@ ul.quick-info li:not(:last-child)::after { li .by { font-style: oblique; - max-width: 600px; } li .by a { @@ -1973,8 +1965,8 @@ p code { #content blockquote { margin-left: 40px; - max-width: 600px; - margin-right: 0; + margin-right: min(8vw, 75px); + width: auto; } #content blockquote blockquote { @@ -2021,7 +2013,6 @@ main.long-content > h1 { dl dt { padding-left: 40px; - max-width: 600px; } dl dt { @@ -2061,6 +2052,15 @@ ul > li.has-details { margin-left: 0; } +.album-group-list li { + padding-left: 1.5ch; + text-indent: -1.5ch; +} + +.album-group-list li > * { + text-indent: 0; +} + .album-group-list blockquote { max-width: 540px; margin-bottom: 9px; @@ -3550,7 +3550,7 @@ main.long-content .content-sticky-heading-container .content-sticky-subheading-r /* Layout - Wide (most computers) */ -@media (min-width: 900px) { +@media (min-width: 850px) { #page-container.showing-sidebar-left:not(.sidebars-in-content-column) #secondary-nav:not(.always-visible), #page-container.showing-sidebar-right:not(.sidebars-in-content-column) #secondary-nav:not(.always-visible) { display: none; @@ -3564,7 +3564,7 @@ main.long-content .content-sticky-heading-container .content-sticky-subheading-r * if so desired. */ -@media (min-width: 600px) and (max-width: 899.98px) { +@media (min-width: 600px) and (max-width: 849.98px) { /* Medium layout is mainly defined (to the user) by hiding the sidebar, so * don't apply the similar layout change of widening the long-content area * if this page doesn't have a sidebar to hide in the first place. @@ -3595,7 +3595,8 @@ main.long-content .content-sticky-heading-container .content-sticky-subheading-r #artwork-column { float: right; width: 40%; - max-width: 400px; + min-width: 220px; + max-width: 280px; margin: -60px 0 10px 20px; position: relative; @@ -3624,7 +3625,7 @@ main.long-content .content-sticky-heading-container .content-sticky-subheading-r /* Layout - Medium or Thin */ -@media (max-width: 899.98px) { +@media (max-width: 849.98px) { .sidebar.collapsible, .sidebar-box-joiner.collapsible, .sidebar-column.all-boxes-collapsible { diff --git a/src/strings-default.yaml b/src/strings-default.yaml index fe40e767..ef885e00 100644 --- a/src/strings-default.yaml +++ b/src/strings-default.yaml @@ -931,9 +931,21 @@ misc: source.customLabel: >- {LABEL} via {SOURCE} - customLabelAlone: >- + source.withYear: >- + Via {SOURCE} ({YEAR}) + + source.customLabel.withYear: >- + {LABEL} ({YEAR}) via {SOURCE} + + customLabel: >- {LABEL} + customLabel.withYear: >- + {LABEL} ({YEAR}) + + year: >- + Released {YEAR} + trackArtFromAlbum: "Album cover for {ALBUM}" sameTagsAsMainArtwork: "Same tags as main artwork" |