diff --git a/make-pages/GalleryPage.hs b/make-pages/GalleryPage.hs
index 1839c6a..9a8b6af 100644
--- a/make-pages/GalleryPage.hs
+++ b/make-pages/GalleryPage.hs
@@ -131,22 +131,27 @@ makeYearItems nsfw year infos = [b|@4
year' = show year & foldMap \c -> [b|$c|]
makeItem :: Bool -> FilePath -> Info -> Builder
-makeItem nsfw file info@(Info {title, bg, date}) = [b|@4
-
+makeItem nsfw file info@(Info {title, bg}) = [b|@4
+
|]
where
- dir = takeDirectory file
- thumb = getThumb dir info
- nsfw' = if nsfw && #anyNsfw info then [b| nsfw|] else ""
- tags' = fold $ intersperse ";" $ map fromText $ tagsFor nsfw info
- date' = formatDateShort date
- bgStyle = ifJust bg \col -> [b| style="background: $col"|]
+ dir = takeDirectory file
+ thumb = getThumb dir info
+ nsfw' = if nsfw && #anyNsfw info then [b| nsfw|] else ""
+ tags' = fold $ intersperse ";" $ map fromText $ tagsFor nsfw info
+ date' = formatDateShort $ #latestDate info
+ updated' = if #updated info then [b|true|] else [b|false|]
+ bgStyle = ifJust bg \col -> [b| style="background: $col"|]
formatDateShort :: Time.Day -> Builder
formatDateShort date = [b|$day $month|] where
diff --git a/make-pages/Info.hs b/make-pages/Info.hs
index 66ac8da..59349ae 100644
--- a/make-pages/Info.hs
+++ b/make-pages/Info.hs
@@ -19,6 +19,7 @@ import Data.Foldable (find)
import Data.Hashable (Hashable)
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
+import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (isJust, isNothing)
import Data.List (nub)
@@ -40,6 +41,7 @@ data Info =
-- e.g. multiple things on the same day might have a,b,c in @sortEx@ to
-- put them in the right order in the gallery
sortEx :: !Text,
+ updates :: !(Map Day Text),
title :: !Text,
artist :: !(Maybe Artist), -- nothing = me, obv
nsfwOnly :: !Bool,
@@ -109,6 +111,11 @@ instance HasField "year" Info Integer where getField = #first . #dmy
instance HasField "month" Info Int where getField = #second . #dmy
instance HasField "day" Info Int where getField = #third . #dmy
+instance HasField "latestDate" Info Day where
+ getField (Info {date, updates}) = maximum (date : Map.keys updates)
+
+instance HasField "updated" Info Bool where getField = not . Map.null . #updates
+
descFor :: Bool -> Info -> Maybe Text
descFor nsfw (Info {desc, nsfwDesc}) = desc <> (guard nsfw *> nsfwDesc)
@@ -122,7 +129,7 @@ linksFor :: Bool -> Info -> [Link]
linksFor nsfw = if nsfw then #links else #sfwLinks
instance Ord Info where
- compare = comparing \Info {date, sortEx, title} -> (date, sortEx, title)
+ compare = comparing \i -> (#latestDate i, #sortEx i, #title i)
newtype NoThumb = NoThumb FilePath
@@ -150,7 +157,8 @@ addSuffix suf path =
instance FromYAML Info where
parseYAML = YAML.withMap "info" \m ->
Info <$> m .: "date"
- <*> m .:? "sort" .!= ""
+ <*> m .:? "sort" .!= ""
+ <*> m .:? "updates" .!= []
<*> m .: "title"
<*> m .:? "artist"
<*> m .:? "nsfw-only" .!= False
diff --git a/make-pages/SinglePage.hs b/make-pages/SinglePage.hs
index 8b4302e..48cda1c 100644
--- a/make-pages/SinglePage.hs
+++ b/make-pages/SinglePage.hs
@@ -5,10 +5,11 @@ import BuilderQQ
import Records ()
import Control.Exception
+import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe)
import qualified Data.Text as Strict
import qualified Data.Text.Lazy as Lazy
-import qualified Data.Time.Calendar as Time
+import qualified Data.Time as Time
import System.FilePath (joinPath, splitPath, (>))
import qualified System.Process as Proc
import Text.Read (readMaybe)
@@ -34,7 +35,8 @@ make root prefix nsfw dataDir dir info =
toLazyText <$> make' root prefix nsfw dataDir dir info
make' :: Text -> FilePath -> Bool -> FilePath -> FilePath -> Info -> IO Builder
-make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
+make' root prefix nsfw dataDir dir
+ info@(Info {date, title, artist, bg, updates}) = do
images <- withSizes (dataDir > dir) $ imagesFor nsfw info
let undir = joinPath (replicate (length (splitPath dir)) "..")
@@ -53,6 +55,7 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
let descSection = makeDesc $ descFor nsfw info
let tagsList = makeTags undir $ tagsFor nsfw info
let linksList = extLinks $ linksFor nsfw info
+ let updatesList = makeUpdates $ Map.toList updates
let makePrefetch (Image {path}) = [b||]
let prefetches = map (makePrefetch . #first) $ tail images
@@ -76,6 +79,9 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
Nothing -> "by niss"
let thumb = getThumb "" info
+ let updateDate = ifJust (Map.lookupMax updates) \(formatDate -> u, _) ->
+ [b|
updated $u|]
+
pure [b|@0
@@ -103,7 +109,9 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
$title
$artistTag
- $formattedDate
+
+ $formattedDate $updateDate
+
$buttonBar
@@ -122,6 +130,8 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
$tagsList
$linksList
+
+ $updatesList
@@ -217,6 +227,25 @@ extLink (Link {title, url}) = [b|@8
|]
+makeUpdates :: [(Day, Text)] -> Builder
+makeUpdates ups =
+ if null ups then "" else [b|@4
+
+ updates
+
+ $8.updateList
+
+
+ |]
+ where updateList = map (uncurry makeUpdate) ups
+
+makeUpdate :: Day -> Text -> Builder
+makeUpdate date txt = [b|@8
+ $date'
+ $txt
+ |]
+ where date' = Time.formatTime Time.defaultTimeLocale "%-d/%-m/%Y" date
+
formatDate :: Day -> Builder
formatDate date = [b|$week $day $month $year|] where
(year, month', day') = Time.toGregorian date
diff --git a/style/cake.svg b/style/cake.svg
new file mode 100644
index 0000000..6900dc1
--- /dev/null
+++ b/style/cake.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/style/shiny/gallery.css b/style/shiny/gallery.css
index 659d3fd..aa27625 100644
--- a/style/shiny/gallery.css
+++ b/style/shiny/gallery.css
@@ -116,17 +116,12 @@ body {
box-shadow: var(--focus-box);
}
-.item[data-date]::before {
- content: attr(data-date);
-}
-
-
figure {
margin: 0;
padding: 0;
}
-figcaption, .item::before {
+figcaption .date, figcaption .title {
position: absolute;
width: 100%;
border: 1px solid var(--text-col);
@@ -134,10 +129,11 @@ figcaption, .item::before {
text-align: center;
background: hsl(0, 0%, 0%, 75%);
font-size: 80%;
+ text-shadow: none;
}
-figcaption { bottom: -1px; left: -1px; }
-.item::before { top: -1px; left: -1px; }
+figcaption .date { top: -1px; left: -1px; }
+figcaption .title { bottom: -1px; left: -1px; }
.year-marker {
grid-area: auto / 1;
@@ -164,19 +160,31 @@ figcaption { bottom: -1px; left: -1px; }
transform: scaleX(110%);
}
-.item.nsfw::after {
- --size: calc(1/4 * var(--image-size));
- content: url(../18_plus_white.svg);
+.item.nsfw::before, .item[data-updated="true"]::after {
height: var(--size);
width: var(--size);
display: inline-block;
position: absolute;
+
+}
+
+.item.nsfw::before {
+ --size: calc(1/4 * var(--image-size));
+ --base-transform: rotate(var(--nsfw-sticker-rotate));
+ content: url(../18_plus_white.svg);
top: calc(1em + 3px);
right: 3px;
-
- transform: rotate(var(--nsfw-sticker-rotate));
mix-blend-mode: multiply;
+ transform: var(--base-transform);
+}
+
+.item[data-updated="true"]::after {
+ --size: calc(1/4 * var(--image-size));
+ content: url(../sparkles.svg);
+ bottom: calc(1em + 3px);
+ right: 3px;
+ mix-blend-mode: overlay;
}
footer {
@@ -187,30 +195,33 @@ footer {
@media (hover) {
- .item:hover figcaption, .item:hover::before {
+ .item:hover .date, .item:hover .title,
+ .item:hover::before, .item:hover::after {
filter: opacity(20%);
}
- @media (prefers-reduced-motion) {
- .item:hover figcaption {
- transform: translateY(80%);
- }
- }
-
@media (prefers-reduced-motion: no-preference) {
- figcaption, .item::before {
+ figcaption .date, figcaption .title, .item::before, .item::after {
transition-property: filter, transform;
- transition-duration: 0.25s;
+ transition-duration: 0.15s;
transition-timing-function: ease-in-out;
}
- .item:hover figcaption {
+ .item:hover .title {
transform: translate(-20%, 80%) rotateZ(7deg);
}
- .item:hover::before {
+ .item:hover .date {
transform: translate(20%, -80%) rotateZ(7deg);
}
+
+ .item:hover::before {
+ transform: translate(1.5em, -1.5em) var(--base-transform);
+ }
+
+ .item:hover::after {
+ transform: translate(1.5em, 1.5em);
+ }
}
}
@@ -219,10 +230,14 @@ footer {
height: min-content;
}
- figcaption {
+ figcaption .date, figcaption .title {
position: initial;
}
+ figcaption .date::after {
+ content: ':';
+ }
+
.item a {
display: block;
height: var(--image-size);
diff --git a/style/shiny/single.css b/style/shiny/single.css
index 55a4530..59b3f95 100644
--- a/style/shiny/single.css
+++ b/style/shiny/single.css
@@ -132,6 +132,28 @@ body {
margin: 0.35em;
}
+#updates dl {
+ display: grid;
+ grid-template-columns: min-content auto;
+ grid-gap: 0.5em;
+ align-items: baseline;
+}
+
+#updates dt {
+ font-size: 80%;
+ font-weight: 700;
+ grid-area: auto / 1;
+}
+
+#updates dd {
+ grid-area: auto / 2;
+}
+
+.updated {
+ font-size: 90%;
+ font-style: italic;
+}
+
footer {
text-align: center;
margin-top: 1em;
diff --git a/style/sparkles.svg b/style/sparkles.svg
new file mode 100644
index 0000000..030419b
--- /dev/null
+++ b/style/sparkles.svg
@@ -0,0 +1 @@
+
\ No newline at end of file