diff --git a/base/article/routes.mjs b/base/article/routes.mjs index d5c88ab..51e8f52 100644 --- a/base/article/routes.mjs +++ b/base/article/routes.mjs @@ -23,8 +23,12 @@ export default class ArticleRoutes { } /** GET: /api/articles/[path] */ - async getArticle(ctx) { + async getArticle(ctx, onlyReturn = false) { let res = await ctx.db.safeCallProc('article_get_single', [ctx.params.path]) + + if (onlyReturn) { + return this.getArticle_resOutput(res) + } ctx.body = this.getArticle_resOutput(res) } diff --git a/base/serve.mjs b/base/serve.mjs index eb6d44f..26a3d56 100644 --- a/base/serve.mjs +++ b/base/serve.mjs @@ -22,7 +22,7 @@ export default class ServeHandler { } let indexFile = fsSync.readFileSync(path.join(this.root, 'index.html')) - this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce'] }) + this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner'] }) // console.log(indexFile.toString()) } diff --git a/base/server.mjs b/base/server.mjs index f8f691e..b7a6d55 100644 --- a/base/server.mjs +++ b/base/server.mjs @@ -7,6 +7,7 @@ import PageRoutes from './page/routes.mjs' import ArticleRoutes from './article/routes.mjs' import AuthenticationRoutes from './authentication/routes.mjs' import { authenticate } from './authentication/security.mjs' +import StaticRoutes from './static_routes.mjs' export default class Server { constructor(http, port, core, opts = {}) { @@ -30,6 +31,7 @@ export default class Server { page: new PageRoutes(), article: new ArticleRoutes(), auth: new AuthenticationRoutes(), + static: new StaticRoutes(), } this.init() @@ -54,6 +56,9 @@ export default class Server { ctx.db = pool }) this.flaska.before(QueryHandler()) + + let healthChecks = 0 + let healthCollectLimit = 60 * 60 * 12 this.flaska.after(function(ctx) { let ended = new Date().getTime() @@ -69,6 +74,18 @@ export default class Server { level = 'error' } + if (ctx.url === '/health') { + healthChecks++ + if (healthChecks >= healthCollectLimit) { + ctx.log[level]({ + duration: Math.round(ended), + status: ctx.status, + }, `<-- ${status}${ctx.method} ${ctx.url} {has happened ${healthChecks} times}`) + healthChecks = 0 + } + return + } + ctx.log[level]({ duration: requestTime, status: ctx.status, diff --git a/base/static_routes.mjs b/base/static_routes.mjs new file mode 100644 index 0000000..ebf8e27 --- /dev/null +++ b/base/static_routes.mjs @@ -0,0 +1,17 @@ +import config from './config.mjs' + +export default class StaticRoutes { + constructor(opts = {}) { + Object.assign(this, { }) + } + + register(server) { + server.flaska.get('/health', this.health.bind(this)) + } + + health(ctx) { + ctx.body = { + environment: config.get('NODE_ENV'), + } + } +} diff --git a/nfp_moe/api/file/util.mjs b/nfp_moe/api/file/util.mjs index b804ed5..958821e 100644 --- a/nfp_moe/api/file/util.mjs +++ b/nfp_moe/api/file/util.mjs @@ -18,7 +18,7 @@ export function combineFilesWithArticles(articles, files) { } export function parseFile(file) { - file.url = 'https://cdn.nfp.is' + file.path + file.path = 'https://cdn.nfp.is' + file.path file.magnet = null file.meta = JSON.parse(file.meta || '{}') || {} if (file.meta.torrent) { @@ -27,6 +27,7 @@ export function parseFile(file) { + '&dn=' + encodeURIComponent(file.meta.torrent.name) + '&xt=urn:btih:' + file.meta.torrent.hash + file.meta.torrent.announce.map(item => ('&tr=' + encodeURIComponent(item))).join('') + file.meta.torrent = { name: file.meta.torrent.name, files: file.meta.torrent.files } } return file } \ No newline at end of file diff --git a/nfp_moe/api/serve.mjs b/nfp_moe/api/serve.mjs index df7490e..9b4636a 100644 --- a/nfp_moe/api/serve.mjs +++ b/nfp_moe/api/serve.mjs @@ -1,8 +1,22 @@ - +import path from 'path' import Parent from '../base/serve.mjs' +import fs from 'fs/promises' +import dot from 'dot' export default class ServeHandler extends Parent { + traverseTree(set, tree) { + for (let branch of tree) { + if (branch.children && branch.children.length) { + set.add(branch.id) + this.traverseTree(set, branch.children) + } + } + } + async serveIndex(ctx) { + let indexFile = await fs.readFile(path.join(this.root, 'index.html')) + this.template = dot.template(indexFile.toString(), { argName: ['headerDescription', 'headerImage', 'headerTitle', 'headerUrl', 'payloadData', 'payloadTree', 'version', 'nonce', 'type', 'banner', 'media'] }) + let payload = { headerDescription: 'Small fansubbing and scanlation group translating and encoding our favourite shows from Japan.', headerImage: this.frontend + '/assets/img/heart.png', @@ -12,18 +26,39 @@ export default class ServeHandler extends Parent { payloadTree: null, version: this.version, nonce: ctx.state.nonce, + type: 'page', + banner: false, + media: false, } try { - payload.payloadTree = JSON.stringify(await this.pageRoutes.getPageTree(ctx, true)) + let tree = await this.pageRoutes.getPageTree(ctx, true) + let setOfBranches = new Set() + this.traverseTree(setOfBranches, tree.tree) + + payload.payloadTree = JSON.stringify(tree) if (ctx.url === '/' || (ctx.url.startsWith('/page/') && ctx.url.lastIndexOf('/') === 5)) { ctx.params.path = null if (ctx.url.lastIndexOf('/') === 5) { ctx.params.path = ctx.url.slice(ctx.url.lastIndexOf('/') + 1) } - payload.payloadData = JSON.stringify(await this.pageRoutes.getPage(ctx, true)) + let data = await this.pageRoutes.getPage(ctx, true) + if (!data.page) { + payload.type = 'frontpage' + } + else if (setOfBranches.has(data.page.id)) { + payload.type = 'page_with_children' + } + payload.media = data.page?.media_avif_preview || false + payload.banner = data.featured?.banner_avif_preview || data.page?.banner_avif_preview || false + payload.payloadData = JSON.stringify(data) + } else if (ctx.url.startsWith('/article/') && ctx.url.lastIndexOf('/') === 8) { + ctx.params.path = ctx.url.slice(ctx.url.lastIndexOf('/') + 1) + let data = await this.articleRoutes.getArticle(ctx, true) + payload.media = data.article?.media_avif_preview || false + payload.payloadData = JSON.stringify(data) + payload.type = 'article' } - console.log('url', ctx.url) } catch (e) { ctx.log.error(e) } diff --git a/nfp_moe/app/fileinfo.js b/nfp_moe/app/fileinfo.js index a5cca90..384eed1 100644 --- a/nfp_moe/app/fileinfo.js +++ b/nfp_moe/app/fileinfo.js @@ -60,7 +60,7 @@ const Fileinfo = { m('a', { target: '_blank', rel: 'noopener', - href: vnode.attrs.file.url, + href: vnode.attrs.file.path, }, this.getDownloadName(vnode)), vnode.attrs.file.magnet ? m('a', { diff --git a/nfp_moe/app/index.js b/nfp_moe/app/index.js index e2c4e52..f3a45fb 100644 --- a/nfp_moe/app/index.js +++ b/nfp_moe/app/index.js @@ -47,4 +47,4 @@ AVIF.onload = AVIF.onerror = function () { m.route(document.getElementById('main'), '/', allRoutes) m.mount(document.getElementById('footer'), Footer) } -AVIF.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A='; +AVIF.src = 'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAABcAAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQAMAAAAABNjb2xybmNseAABAA0ABoAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAAB9tZGF0EgAKCBgAPkgIaDQgMgkf8AAAQAAAr3A='; diff --git a/nfp_moe/app/media.js b/nfp_moe/app/media.js index c90cdea..fcb8541 100644 --- a/nfp_moe/app/media.js +++ b/nfp_moe/app/media.js @@ -1,3 +1,5 @@ +const m = require('mithril') + export function generatePictureSource(item, cover) { if (!item || !item.media_alt_prefix) return null @@ -10,9 +12,56 @@ export function generatePictureSource(item, cover) { + item.media_alt_prefix + '_medium.avif' + ' 1300w, ' + item.media_alt_prefix + '_large.avif 1920w', cover: cover, + preview: item.media_avif_preview, } } +let loadingImage = null +let loader = null + +function cancelLoader() { + if (loader) { + loader.src = '' + } + loader = null +} + +export function smartBanner(item) { + if (!item) { + if (loader) { + cancelLoader() + } + loadingImage = null + return null + } + + if (!item.preview) { + loadingImage = null + cancelLoader() + return item.banner + } + if (loadingImage !== item.banner && loader) { + cancelLoader() + } + if (loadingImage === item.banner && !loader) { + return item.banner + } + if (loadingImage === item.banner) { + return item.preview + } + + loadingImage = item.banner + loader = new Image(); + + loader.src = item.banner; + loader.onload = loader.onerror = function() { + loader = null + m.redraw() + } + + return item.preview +} + export function getBannerImage(item, prefix) { if (!item || !item.banner_alt_prefix) return null @@ -20,7 +69,8 @@ export function getBannerImage(item, prefix) { path: prefix + item.path, name: item.name, original: item.banner_path, - banner: item.banner_alt_prefix + banner: item.banner_alt_prefix, + preview: item.banner_avif_preview, } var deviceWidth = window.innerWidth @@ -47,23 +97,24 @@ export function getArticlePicture(pictureData, useRouteLink, path, altText, fall if (!pictureData) return fallback || null return m(useRouteLink ? m.route.Link : 'a', { - class: 'cover', + class: 'cover ' + (pictureData.preview ? 'haspreview' : ''), rel: useRouteLink ? null : 'noopener', target: useRouteLink ? null : '_blank', href: path, - }, - m('picture', [ - m('source', { - srcset: pictureData.avif, - sizes: pictureData.cover, - type: 'image/avif', - }), - m('img', { - srcset: pictureData.jpeg, - sizes: pictureData.cover, - alt: altText, - src: pictureData.fallback, - }), + }, [ + pictureData.preview ? m('img', { src: pictureData.preview }) : null, + m('picture', [ + m('source', { + srcset: pictureData.avif, + sizes: pictureData.cover, + type: 'image/avif', + }), + m('img', { + srcset: pictureData.jpeg, + sizes: pictureData.cover, + alt: altText, + src: pictureData.fallback, + }), + ]) ]) -) } \ No newline at end of file diff --git a/nfp_moe/app/site_article.js b/nfp_moe/app/site_article.js index 9dd39ec..ea19ca3 100644 --- a/nfp_moe/app/site_article.js +++ b/nfp_moe/app/site_article.js @@ -21,7 +21,7 @@ const SiteArticle = { if (window.__nfpdata) { this.path = m.route.param('id') - this.data.article = window.__nfpdata + this.data = window.__nfpdata window.__nfpdata = null this.afterData() } else { @@ -83,7 +83,30 @@ const SiteArticle = { return [ this.loading - ? m('div.loading-spinner') + ? m('.inside.vertical', [ + m('div.actions', m('div.lb-link')), + m('article.fullsize', [ + m('h2.title', m('div.lb-main.lb--long')), + m('div.row', [ + m('div.cover', m('picture.lb.nobg')), + m('div.description', [ + m('div.lb-main.lb--long'), + m('div.lb-main'), + m('div.lb-main.lb--medium'), + m('div.lb-main.lb--medium'), + m('div.lb-main'), + m('p', m.trust(' ')), + m('fileinfo', [ + m('div.lb-main.lb--slim.lb--longest'), + m('ul', [ + m('li', m('div.lb-main.lb--slim.lb--longest')), + m('li', m('div.lb-main.lb--slim.lb--longest')), + ]) + ]), + ]), + ]), + ]), + ]) : null, !this.loading && this.error === 'Article not found' ? NotFoundView.view() @@ -96,7 +119,7 @@ const SiteArticle = { }, }, 'Article error: ' + this.error + '. Click here to try again')) : null, - (article + (!this.loading && article ? m('.inside.vertical', [ m('div.actions', [ '« ', @@ -134,6 +157,7 @@ const SiteArticle = { } }}, m('div.loading-spinner')) : m('button.comments', { + style: 'display: none', onclick: function() { window.LoadComments = true }, }, 'Open comment discussion'), ]) diff --git a/nfp_moe/app/site_page.js b/nfp_moe/app/site_page.js index 1d8a0e4..96fde77 100644 --- a/nfp_moe/app/site_page.js +++ b/nfp_moe/app/site_page.js @@ -15,7 +15,7 @@ const SitePage = { oninit: function(vnode) { this.error = '' this.loading = false - this.showLoading = null + this.treePage = null this.data = { page: null, articles: [], @@ -26,7 +26,6 @@ const SitePage = { this.children = [] this.currentPage = Number(m.route.param('page')) || 1 - console.log('test', window.__nfpdata) if (window.__nfpdata) { this.path = m.route.param('id') this.lastpage = this.currentPage @@ -58,24 +57,13 @@ const SitePage = { this.lastpage = this.currentPage this.path = m.route.param('id') - if (this.showLoading) { - clearTimeout(this.showLoading) - } - - if (this.data.page) { - this.showLoading = setTimeout(() => { - this.showLoading = null - this.loading = true - m.redraw() - }, 300) - } else { - this.loading = true - } + this.loading = true if (this.path) { - this.children = PageTree.TreeMap.get(this.path) - this.children = this.children && this.children.children || [] + this.treePage = PageTree.TreeMap.get(this.path) + this.children = this.treePage && this.treePage.children || [] } else { + this.treePage = { has_banner: true, has_media: true } this.children = PageTree.Tree } @@ -90,8 +78,6 @@ const SitePage = { this.error = err.message }) .then(() => { - clearTimeout(this.showLoading) - this.showLoading = null this.loading = false m.redraw() }) @@ -111,8 +97,8 @@ const SitePage = { } this.picture = media.generatePictureSource(this.data.page, - '(max-width: 840px) calc(100vw - 82px), ' - + '758px') + '(max-width: 1280px) calc(100vw - 2rem), ' + + '1248px') if (this.lastpage !== 1) { document.title = 'Page ' + this.lastpage + ' - ' + title @@ -128,107 +114,211 @@ const SitePage = { return ([ this.loading - ? m('div.loading-spinner') - : null, - !this.loading && this.error === 'Page not found' - ? NotFoundView.view() - : null, - !this.loading && this.error && this.error !== 'Page not found' - ? m('div.wrapper', m('div.error', { - onclick: () => { - this.error = '' - this.fetchPage(vnode) - }, - }, 'Page error: ' + this.error + '. Click here to try again')) - : null, - (featuredBanner - ? m(m.route.Link, { - class: 'page-banner', - href: featuredBanner.path, - style: { 'background-image': 'url("' + featuredBanner.banner + '")' }, - }, - m('div.inside', m('div.page-banner-title', featuredBanner.name)) - ) - : null), - (!featuredBanner && pageBanner - ? m('a.page-banner', { - href: pageBanner.original, - target: '_blank', - style: { 'background-image': 'url("' + pageBanner.banner + '")' }, - }, - ) - : null), - (page - ? m('.inside.vertical', [ - m('div.actions', [ - '« ', - m(m.route.Link, { - href: page.parent_path - ? '/page/' + page.parent_path - : '/' - }, page.parent_name || 'Home'), - Authentication.currentUser - ? [ - m('div.filler'), - 'Actions:', - m(m.route.Link, { href: '/admin/pages/' + page.id }, 'Edit page'), - ] - : null, - ]), - m('h2.title', page.name) - ]) - : null), - (page || this.data.articles.length - ? m('.inside', [ - this.children.length - ? m('aside', { class: page ? '' : 'frontpage' }, [ - m('h5', page ? 'View ' + page.name + ':' : 'Categories'), - this.children.map((page) => { - return [ - m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name), - (page.children && page.children.length - ? m('ul', page.children.map(function(subpage) { - return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name)) - })) - : null), - ] - }), - !page - ? m('div.asuna.spritesheet') - : null, + ? [ + this.treePage.has_banner + ? m('div.page-banner.lb') + : null, + this.path + ? m('div.inside.vertical', [ + m('div.actions', m('div.lb-link')), + m('h2.title', m('div.lb-main.lb--long')), ]) : null, - m('div.container', [ - (page - ? media.getArticlePicture(this.picture, false, page.media_path, 'Image for page ' + page.name) - : null), - (page && page.content - ? m('div.content', page.content.blocks.map(block => { - return m(EditorBlock, { block: block }) - })) - : null), - (page && this.data.articles.length - ? [ - m('h5', 'Latest posts under ' + page.name + ':'), - this.data.articles.map(function(article) { - return m(Articleslim, { article: article }) - }), - ] - : null), - (!page && this.data.articles.length - ? this.data.articles.map(function(article) { - return m(Article, { article: article }) - }) - : null), - m(Paginator, { - base: page ? '/page/' + page.path : '/', - page: this.currentPage, - perPage: ArticlesPerPage, - total: this.data.total_articles, - }), + m('div.inside', [ + this.children.length + ? m('aside', [ + m('h5', m('div.lb-main')), + m('div.lb-link'), + m('ul', [ + m('li', m('div.lb-link')), + m('li', m('div.lb-link')), + m('li', m('div.lb-link')), + ]), + m('div.lb-link'), + m('ul', [ + m('li', m('div.lb-link')), + m('li', m('div.lb-link')), + m('li', m('div.lb-link')), + ]), + ]) + : null, + !this.path + ? m('div.container', [ + m('article', [ + m('h2.title', m('div.lb-main.lb--long')), + m('div.row', [ + m('div.lb-main.lb--imgmini'), + m('div', [ + m('div', m.trust(' ')), + m('div.lb-main.lb--long'), + m('div.lb-main'), + m('div.lb-main.lb--medium'), + m('div.lb-main.lb--medium'), + m('div.lb-main'), + ]), + ]), + ]), + m('article', [ + m('h2.title', m('div.lb-main.lb--long')), + m('div.row', [ + m('div.lb-main.lb--imgmini'), + m('div', [ + m('div', m.trust(' ')), + m('div.lb-main.lb--long'), + m('div.lb-main'), + m('div.lb-main.lb--medium'), + m('div.lb-main.lb--medium'), + m('div.lb-main'), + ]), + ]), + ]), + ]) + : null, + this.path + ? m('div.container', [ + this.treePage.has_media + ? m('div.cover', m('picture.lb.nobg')) + : null, + m('h5', m('div.lb-main.lb--long')), + m('articleslim', [ + m('a.cover.nobg.lb'), + m('div', [ + m('div', m('div.lb-link.lb--long')), + m('div.lb-main.lb--longest.lb--slim'), + ]), + ]), + m('articleslim', [ + m('a.cover.nobg.lb'), + m('div', [ + m('div', m('div.lb-link.lb--long')), + m('div.lb-main.lb--longest.lb--slim'), + ]), + ]), + m('articleslim', [ + m('a.cover.nobg.lb'), + m('div', [ + m('div', m('div.lb-link.lb--long')), + m('div.lb-main.lb--longest.lb--slim'), + ]), + ]), + m('articleslim', [ + m('a.cover.nobg.lb'), + m('div', [ + m('div', m('div.lb-link.lb--long')), + m('div.lb-main.lb--longest.lb--slim'), + ]), + ]), + ]) + : null, ]), - ]) - : null), + ] + : [ + this.error === 'Page not found' + ? NotFoundView.view() + : null, + this.error && this.error !== 'Page not found' + ? m('div.wrapper', m('div.error', { + onclick: () => { + this.error = '' + this.fetchPage(vnode) + }, + }, 'Page error: ' + this.error + '. Click here to try again')) + : null, + (featuredBanner + ? m(m.route.Link, { + class: 'page-banner', + href: featuredBanner.path, + style: { 'background-image': 'url("' + featuredBanner.preview + '")' }, + }, [ + m('div.page-banner-real', { + style: { 'background-image': 'url("' + featuredBanner.banner + '")' }, + }, m('div.inside', m('div.page-banner-title', featuredBanner.name))), + ] + ) + : null), + (!featuredBanner && pageBanner + ? m('a.page-banner', { + href: pageBanner.original, + target: '_blank', + style: { 'background-image': 'url("' + pageBanner.preview + '")' }, + }, + m('div.page-banner-real', { + style: { 'background-image': 'url("' + pageBanner.banner + '")' }, + }) + ) + : null), + (page + ? m('.inside.vertical', [ + m('div.actions', [ + '« ', + m(m.route.Link, { + href: page.parent_path + ? '/page/' + page.parent_path + : '/' + }, page.parent_name || 'Home'), + Authentication.currentUser + ? [ + m('div.filler'), + 'Actions:', + m(m.route.Link, { href: '/admin/pages/' + page.id }, 'Edit page'), + ] + : null, + ]), + m('h2.title', page.name) + ]) + : null), + (page || this.data.articles.length + ? m('.inside', [ + this.children.length + ? m('aside', { class: page ? '' : 'frontpage' }, [ + m('h5', page ? 'View ' + page.name + ':' : 'Categories'), + this.children.map((page) => { + return [ + m(m.route.Link, { class: 'root', href: '/page/' + page.path }, page.name), + (page.children && page.children.length + ? m('ul', page.children.map(function(subpage) { + return m('li', m(m.route.Link, { class: 'child', href: '/page/' + subpage.path }, subpage.name)) + })) + : null), + ] + }), + !page + ? m('div.asuna.spritesheet') + : null, + ]) + : null, + m('div.container', [ + (page + ? media.getArticlePicture(this.picture, false, page.media_path, 'Image for page ' + page.name) + : null), + (page && page.content + ? m('div.content', page.content.blocks.map(block => { + return m(EditorBlock, { block: block }) + })) + : null), + (page && this.data.articles.length + ? [ + m('h5', 'Latest posts under ' + page.name + ':'), + this.data.articles.map(function(article) { + return m(Articleslim, { article: article }) + }), + ] + : null), + (!page && this.data.articles.length + ? this.data.articles.map(function(article) { + return m(Article, { article: article }) + }) + : null), + m(Paginator, { + base: page ? '/page/' + page.path : '/', + page: this.currentPage, + perPage: ArticlesPerPage, + total: this.data.total_articles, + }), + ]), + ]) + : null), + ], ]) }, } diff --git a/nfp_moe/public/assets/admin.css b/nfp_moe/public/assets/admin.css index 4b66699..d8be2d1 100644 --- a/nfp_moe/public/assets/admin.css +++ b/nfp_moe/public/assets/admin.css @@ -306,6 +306,29 @@ dialogue button.cancel { min-height: 80px !important; } + + +@keyframes spinner-loader { + to {transform: rotate(360deg);} +} + +.loading-spinner:after { + content: ''; + box-sizing: border-box; + position: absolute; + top: 50%; + left: 50%; + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + border-radius: 50%; + border: 2px solid #ccc; + border-top-color: #333; + animation: spinner-loader .6s linear infinite; + z-index: 1000; +} + /* ===================== 3rd party ===================== */ diff --git a/nfp_moe/public/assets/app.css b/nfp_moe/public/assets/app.css index d18d94b..d65fbff 100644 --- a/nfp_moe/public/assets/app.css +++ b/nfp_moe/public/assets/app.css @@ -1,858 +1,3 @@ -/* - ===================== Variables ===================== -*/ -:root { - --content-max-width: 1280px; - --primary-darker-bg: #002f6c; - --primary-darker-fg: #fff; - --primary-darker-fg-light: #999; - --primary-darker-link: #ffad42; - - --primary-bg: #3d77c7; - --primary-fg: #fff; - --primary-fg-light: #999; - --primary-link: #f57c00; - - --bg: #fff; - --bg-content-alt: #eee; - --color: #000; - --light: #757575; - --link: #bb4d00; - --title-bg: #f57c00; - --title-fg: #000; - --seperator: #ccc; - --content-bg: #fff; - --content-border: 0px solid transparent; - - --alt-bg: #ccc; - --alt-inside-bg: #fff; - --alt-inside-border: 1px solid #555; - --alt-color: #555; - - --footer-bg: #ccc; - --footer-color: #000; - --footer-seperator: #fff; - --footer-link: #8F3C00; - - --button-border: 1px solid #f57c00; - --button-bg: #ffad42; - --button-fg: #000; - - --error-bg: red; - --error-fg: white; -} - -.nightmode { - --content-max-width: 1280px; - --primary-darker-bg: #002f6c; - --primary-darker-fg: #fff; - --primary-darker-fg-light: #999; - --primary-darker-link: #ffad42; - - --primary-bg: #28518b; - --primary-fg: #fff; - --primary-fg-light: #999; - --primary-link: #f57c00; - - --bg: black; - --bg-content-alt: #333; - --color: #d7dadc; - --light: #bbb; - --link: #e05e00; - --title-bg: #e05e00; - --title-fg: #000; - --title-sublink: #27159C; - --seperator: #ccc; - --content-bg: #1a1a1b; - --content-border: 1px solid #343536; - - --alt-bg: #000; - --alt-inside-bg: #343536; - --alt-inside-border: 1px solid #808080; - --alt-color: #d7dadc; - - --footer-bg: #343536; - --footer-color: #d7dadc; - --footer-seperator: #666; - --footer-link: #fe791b; - - --button-border: 1px solid #f57c00; - --button-bg: #ffad42; - --button-fg: #000; - - --error-bg: red; - --error-fg: white; -} - -/* - ===================== Reset ===================== -*/ - -/* Box sizing rules */ -*, *::before, *::after { box-sizing: border-box; -} - -/* Remove default margin */ -body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { - margin: 0; -} - -body { - min-height: 100vh; - text-rendering: optimizeSpeed; - line-height: 1.5; - font-size: 16px; - font-family: 'Inter var', sans-serif; - font-feature-settings: "slnt" 0deg, "case", "frac", "tnum", "ss02", "calt", "ccmp", "kern"; - background: var(--bg); - color: var(--color); -} - -.italic { font-variation-settings: "slnt" 10deg; } - -a { - text-decoration-skip-ink: auto; -} - -img { - max-width: 100%; - margin: 0 auto; - display: block; -} - -input, button, textarea, select { - font: inherit; -} - -@media (prefers-reduced-motion: reduce) { - *, *::before, *::after { - animation-play-state: paused !important; - transition: none !important; - scroll-behavior: auto !important; - } -} - -h1 { - font-size: 2.488rem; -} -h2 { - font-size: 2.074rem; -} -h3 { - font-size: 1.728rem; -} -h4 { - font-size: 1.44rem; -} -h5 { - font-size: 1.0rem; -} - -a, a:visited, button { - text-decoration: none; - border: none; - padding: 0; - margin: 0; - font-weight: bold; - cursor: pointer; -} - -input[type=text], -input[type=password], -select { - border: 1px solid var(--color); - background: var(--bg); - color: var(--color); - border-radius: 0; - padding: 0.25rem; - line-height: 1rem; -} - -label { - font-size: 0.75rem; - font-weight: 500; - margin-top: 1rem; - margin-bottom: 0.25rem; - display: block; -} - -input[type=text]:hover, -input[type=text]:active, -input[type=password]:hover, -input[type=password]:active, -select:hover, -select:active { - border-color: var(--link); -} - -button { - background: transparent; -} - -/* - ===================== Common ===================== -*/ - -.inside { - width: 100%; - max-width: var(--content-max-width); - display: flex; - margin: 0 auto; -} - -.inside.vertical { - flex-direction: column; -} - -.error { - background: var(--error-bg); - color: var(--error-fg); - cursor: pointer; - text-align: center; - padding: 0.5rem; -} - -.wrapper { - background: var(--alt-bg); - color: var(--alt-color); - display: flex; - justify-content: center; - align-items: center; - min-height: calc(100vh - 200px); - padding: 1rem; -} - -.filler { - flex: 2 1 auto; -} - -.wrapper .inside { - flex-direction: column; - color: var(--alt-color); - background: var(--alt-inside-bg); - border: var(--alt-inside-border); -} - -.wrapper .error { - border: 1px solid var(--error-bg); - color: var(--error-bg); - background: transparent; -} - -@keyframes spinner-loader { - to {transform: rotate(360deg);} -} - -.loading-spinner:after { - content: ''; - box-sizing: border-box; - position: absolute; - top: 50%; - left: 50%; - width: 20px; - height: 20px; - margin-top: -10px; - margin-left: -10px; - border-radius: 50%; - border: 2px solid #ccc; - border-top-color: #333; - animation: spinner-loader .6s linear infinite; - z-index: 1000; -} - -.notfound { - color: var(--light); -} - -@media screen and (max-width: 639px){ - main .inside { - flex-direction: column; - } - - .wrapper { - flex-direction: column; - } -} - -/* - ===================== Header ===================== -*/ - -header { - background: var(--primary-darker-bg); - color: var(--primary-darker-fg); -} - -header a, -header a:visited, -header button { - color: var(--primary-darker-link); -} - -header p { - color: var(--primary-darker-fg-light); -} - -header .title, -header .title:visited { - min-height: 100px; - padding-left: 10px; - display: flex; - align-items: center; - background: 25px center no-repeat; - background-size: auto 91px; - flex: 0 0 auto; -} - -header .logo { - background-position: -119px 0px; - width: 81px; - height: 100px; - transform: scale(0.9); - margin-right: 1rem; -} - -header .title h1 { - font-weight: 500; - color: var(--primary-darker-fg); -} - -header aside { - flex: 2 1 auto; - display: flex; - flex-direction: column; - align-items: flex-end; - font-size: 0.8rem; - padding: 0.5rem 0.5rem; -} - -header aside a, -header aside button { - margin-left: 0.5rem; -} - -header aside p button { - margin-left: 0; -} - -/* - ===================== Nav ===================== -*/ - -nav { - background: var(--primary-bg); -} - -nav a, nav a:visited, nav .loading-spinner { - flex: 2 0 auto; - text-align: center; - font-weight: 300; - padding: 10px 10px 7px 10px; - border-bottom: 3px solid var(--primary-bg); - color: var(--primary-fg); -} - -nav a.active { - border-bottom-color: var(--primary-link); -} - -nav .loading-spinner { - position: relative; -} - -@media screen and (max-width: 639px){ - nav { - font-size: 0.8em; - } -} - -/* - ===================== main ===================== -*/ - -main { - min-height: calc(100vh - 390px); -} - -.page-banner { - background-size: cover; - background-repeat: no-repeat; - background-position: center; - height: 150px; - width: 100%; - display: block; -} - -.page-banner-title { - color: white; - text-align: right; - padding: 0.5rem 1rem; - font-size: 1.6rem; - flex: 2 1 auto; - text-shadow: 0 0 .3em #000; -} - -.actions { - padding: 0.5rem 1rem; - display: flex; -} - -.actions a { - margin-left: 0.375rem; -} - -main a, -main a:visited { - color: var(--link); -} - -main h5 { - padding: 0 0.5rem 0.5rem; - margin: 0 0 0.75rem; - border-bottom: 1px solid var(--seperator); - font-size: 1rem; -} - -main .loading-spinner { - position: fixed; - left: 50%; - top: 50%; -} - -main h2.title, -.main h2.title { - font-size: 1.4rem; - background: var(--title-bg); - color: var(--title-fg); - text-align: center; - font-weight: 400; - padding: 0.375rem; - line-height: 1.4rem; -} - -main .container { - flex: 2 1 auto; - margin: 1rem; -} - -main .cover picture img { - margin-bottom: 1rem; - width: 100%; -} - -main button, -main input[type=submit] { - border: var(--button-border); - background: #ffad42; - color: #000; - align-self: center; - padding: 0.25rem 1rem; - margin: 1rem 0 2rem; -} - -@media screen and (max-width: 639px){ - main .container { - margin: 1rem 0.25rem; - } -} - -/* ************** aside ************** */ - -main aside { - padding: 0.375rem 1rem 0.5rem; - margin: 1rem; - font-size: 0.875rem; - flex: 0 0 250px; - background: var(--content-bg); - border: var(--content-border); -} - -main aside a { - display: block; -} - -main aside h5 { - margin: 0 -0.5rem 0.25rem; - font-size: 0.9em; -} - -main aside ul { - margin: 0 0 0.5rem; - padding-left: 1.5rem; -} - -main aside .asuna { - margin-top: 2rem; - width: 200px; - height: 461px; - background-position: 0 -150px; -} - -.nightmode main aside .asuna { - background-position: -200px -150px; -} - -.daymode .day { - display: block; -} - -@media screen and (max-width: 1000px){ - main aside { - flex: 0 0 200px; - } -} - -@media screen and (max-width: 639px){ - main aside { - margin: 1rem 0.25rem; - flex: 0 0 auto; - } - - main aside.frontpage { - order: 2; - } -} - - -/* ************** paginator ************** */ - -paginator { - display: flex; - justify-content: center; - width: 100%; -} - -paginator a { - color: var(--link); - cursor: pointer; -} - -paginator a, -paginator div { - display: block; - font-size: 0.8rem; - max-width: 80px; - flex-grow: 2; - text-align: center; - padding: 0.5rem; - margin-top: 1rem; -} - -/* ************** articleslim ************** */ - -articleslim { - display: flex; - margin-bottom: 0.75rem; - padding-right: 0.5rem; -} - -articleslim p.description { - font-size: 0.75rem; -} - -articleslim .cover { - flex: 0 0 124px; - margin-right: 0.75rem; -} - -articleslim .cover picture img { - margin-bottom: 0; -} - -articleslim a.nobg { - height: 70px; - background: var(--seperator); - display: block; -} - -articleslim a.title { - display: block; - margin-bottom: 0.375rem; -} - -/* ************** article ************** */ - -article { - background: var(--content-bg); - border: var(--content-border); - margin-bottom: 1rem; -} - -article .row { - margin: 1rem 0; - display: flex; -} - -article .cover { - flex: 0 0 auto; - margin-right: 1rem; - align-self: flex-start; -} - -article a.title { - flex: 0 0 100%; - margin-bottom: 0.5rem; -} - -article .description { - font-size: 0.875rem; - margin-bottom: 1rem; - padding: 0 0.25rem; -} - -article .meta { - font-size: 0.625rem; - line-height: 0.75rem; - color: var(--light); - font-weight: 500; - padding: 1.25rem 0.25rem 0; -} - -article.fullsize .row { - margin: 1rem; - flex-direction: column; -} - -article.fullsize .cover { - margin-right: 0; -} - -@media screen and (max-width: 1000px){ - article .row { - flex-direction: column; - } - - article.fullsize .row { - margin: 1rem 0.25rem; - } - - article .cover { - margin-right: 0; - } -} - -/* ************** fileinfo ************** */ - -fileinfo { - padding-left: 0.25rem; - margin-bottom: 0.5rem; - color: var(--light); - line-height: 1rem; - font-size: 0.75rem; - display: block; - position: relative; -} - -fileinfo.slim { - padding: 0; - margin: 0; -} - -fileinfo p span, -fileinfo p a { - margin-right: 0.25rem; -} - -fileinfo p a { - font-weight: 550; - padding-right: 0.25rem; - border-right: 1px solid var(--seperator); - display: inline-block; -} - -fileinfo p span { - font-weight: 700; -} - -fileinfo .trimmed { - padding: 0.25rem 0 0.25rem 1rem; -} - -fileinfo ul { - margin: 0.5rem 0; - padding-left: 1.5rem; -} - -/* - ===================== login ===================== -*/ - -.login--first { - flex: 0 0 170px; -} - -.login { - align-items: center; - font-size: 1rem; - padding: 1rem 1rem 2rem; - margin: 1rem; - max-width: 400px; -} - -.login .title { - font-size: 1.4rem; - font-weight: 200; - margin-bottom: 2rem; - text-align: center; -} - -.login input, -.login label { - width: 100%; - max-width: 300px; -} - -.login input[type=submit] { - min-width: 150px; - margin-top: 1rem; -} - -.login--asuna { - flex: 0 0 auto; - width: 180px; - height: 494px; - background-position: -400px 0; -} - -.nightmode .login--asuna { - background-position: -580px 0; -} - - -@media screen and (max-width: 1000px){ - .login--first { - display: none; - } -} - - -@media screen and (max-width: 639px){ - .login { - order: 2; - } - - .login--asuna { - max-width: 120px; - } -} - -/* - ===================== content ===================== -*/ - -.content :is(h1, h2, h3, h4, h5, ul, ol, blockquote, p) { - margin: 0 0 0.75rem; -} - -.content :is(h1, h2, h3, h4, h5) { - padding: 0 0.5rem 0.5rem; - border-bottom: 1px solid var(--seperator); -} - -.content :is(blockquote, pre) { - background: var(--bg-content-alt); - padding: 0.5rem; -} - -.content blockquote p { - margin: 0; -} - -/* - ===================== footer ===================== -*/ - -footer { - background: var(--footer-bg); - color: var(--footer-color); - text-align: center; - padding: 1rem; - display: flex; - align-items: center; - font-weight: 500; - font-size: 0.625rem; -} - -footer .first { - flex: 0 0 119px; -} - -footer .middle { - display: flex; - flex-direction: column; - align-items: center; - flex: 2 1 auto; - padding: 0 2rem; -} - -footer .asuna { - flex: 0 0 119px; - height: 150px; - width: 119px; - background-position: 0px 0px; -} - -footer ul { - margin: 0 0 0.25rem; - padding: 0 0 0.25rem; - border-bottom: 1px solid var(--footer-seperator); - display: flex; - justify-content: center; - flex-wrap: wrap; - min-width: 300px; -} - -footer ul li { - padding: 0 0.25rem; - list-style-position: inside; -} - -footer a { - color: var(--footer-link); - margin: 0 0 0.25rem; -} - - -@media screen and (max-width: 1000px){ - footer .first { - display: none; - } -} - - -@media screen and (max-width: 639px){ - footer{ - flex-direction: column; - } - - footer .middle { - padding: 0 0 2rem; - } - - footer .asuna { - flex: 0 0 150px; - } -} - -/* - ===================== 404 page ===================== -*/ - -.not_found { - flex-direction: column; - text-align: center; -} - -.not_found .asuna { - width: 120px; - height: 444px; - margin: 2rem 0 0rem; - background-position: -760px 0; -} - -.nightmode .not_found .asuna { - background-position: -880px 0; -} /* ===================== Large assets ===================== diff --git a/nfp_moe/public/assets/app_body.css b/nfp_moe/public/assets/app_body.css new file mode 100644 index 0000000..38e6cb8 --- /dev/null +++ b/nfp_moe/public/assets/app_body.css @@ -0,0 +1,1039 @@ +/* + ===================== Variables ===================== +*/ +:root { + --content-max-width: 1280px; + --primary-darker-bg: #002f6c; + --primary-darker-fg: #fff; + --primary-darker-fg-light: #999; + --primary-darker-link: #ffad42; + + --primary-bg: #3d77c7; + --primary-fg: #fff; + --primary-fg-light: #999; + --primary-link: #f57c00; + + --bg: #fff; + --bg-content-alt: #eee; + --color: #000; + --light: #757575; + --link: #bb4d00; + --title-bg: #f57c00; + --title-fg: #000; + --seperator: #ccc; + --content-bg: #fff; + --content-border: 0px solid transparent; + + --alt-bg: #ccc; + --alt-inside-bg: #fff; + --alt-inside-border: 1px solid #555; + --alt-color: #555; + + --footer-bg: #ccc; + --footer-color: #000; + --footer-seperator: #fff; + --footer-link: #8F3C00; + + --button-border: 1px solid #f57c00; + --button-bg: #ffad42; + --button-fg: #000; + + --error-bg: red; + --error-fg: white; +} + +.nightmode { + --content-max-width: 1280px; + --primary-darker-bg: #002f6c; + --primary-darker-fg: #fff; + --primary-darker-fg-light: #999; + --primary-darker-link: #ffad42; + + --primary-bg: #28518b; + --primary-fg: #fff; + --primary-fg-light: #999; + --primary-link: #f57c00; + + --bg: black; + --bg-content-alt: #333; + --color: #d7dadc; + --light: #bbb; + --link: #e05e00; + --title-bg: #e05e00; + --title-fg: #000; + --title-sublink: #27159C; + --seperator: #ccc; + --content-bg: #1a1a1b; + --content-border: 1px solid #343536; + + --alt-bg: #000; + --alt-inside-bg: #343536; + --alt-inside-border: 1px solid #808080; + --alt-color: #d7dadc; + + --footer-bg: #343536; + --footer-color: #d7dadc; + --footer-seperator: #666; + --footer-link: #fe791b; + + --button-border: 1px solid #f57c00; + --button-bg: #ffad42; + --button-fg: #000; + + --error-bg: red; + --error-fg: white; +} + +/* + ===================== Reset ===================== +*/ + +/* Box sizing rules */ +*, *::before, *::after { box-sizing: border-box; +} + +/* Remove default margin */ +body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { + margin: 0; +} + +body { + min-height: 100vh; + text-rendering: optimizeSpeed; + line-height: 1.5; + font-size: 16px; + font-family: 'Inter var', sans-serif; + font-variation-settings: "slnt" 0; + font-feature-settings: "case", "frac", "tnum", "ss02", "calt", "ccmp", "kern"; + background: var(--bg); + color: var(--color); +} + +.italic { font-variation-settings: "slnt" 10deg; } + +a { + text-decoration-skip-ink: auto; +} + +img { + max-width: 100%; + margin: 0 auto; + display: block; +} + +input, button, textarea, select { + font: inherit; +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-play-state: paused !important; + transition: none !important; + scroll-behavior: auto !important; + } +} + +h1 { + font-size: 2.488rem; +} +h2 { + font-size: 2.074rem; +} +h3 { + font-size: 1.728rem; +} +h4 { + font-size: 1.44rem; +} +h5 { + font-size: 1.0rem; +} + +a, a:visited, button { + text-decoration: none; + border: none; + padding: 0; + margin: 0; + font-weight: bold; + cursor: pointer; +} + +input[type=text], +input[type=password], +select { + border: 1px solid var(--color); + background: var(--bg); + color: var(--color); + border-radius: 0; + padding: 0.25rem; + line-height: 1rem; +} + +label { + font-size: 0.75rem; + font-weight: 500; + margin-top: 1rem; + margin-bottom: 0.25rem; + display: block; +} + +input[type=text]:hover, +input[type=text]:active, +input[type=password]:hover, +input[type=password]:active, +select:hover, +select:active { + border-color: var(--link); +} + +button { + background: transparent; +} + + +/* + ===================== Loading bar ===================== +*/ + +.lb, +.lb-main, +.lb-link { + width: 100px; + background: var(--color); + opacity: 0.35; + height: 1rem; + border-radius: 20px; + align-self: center; + position: relative; +} + +.lb-main, +.lb-link { + display: inline-block; +} + +.lb-link { + background: var(--link); +} + +.lb:after, +.lb-main:after, +.lb-link:after { + content: ''; + background: repeating-linear-gradient(to right, transparent 0%, transparent 25%, #00000044 75%, transparent 100%); + background-size: 300% auto; + background-position: 0 -300%; + border-radius: 20px; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + animation: lbgradient 10s infinite; + animation-fill-mode: forwards; + animation-timing-function: linear; +} + +.lb-main:after { + background: repeating-linear-gradient(to right, transparent 0%, transparent 25%, #ffffff44 75%, transparent 100%); + background-size: 300% auto; + background-position: 0 -300%; +} + +.lb-link:after { + background: repeating-linear-gradient(to right, transparent 0%, transparent 25%, #ffffff44 75%, transparent 100%); + background-size: 300% auto; + background-position: 0 -300%; +} + +@keyframes lbgradient { + 0% { background-position: 300% 0; } + 100% { background-position: -300% 0; } +} + +main aside .lb-main { + height: 0.875rem; +} + +.actions .lb-link { + margin: 0.25rem 0; +} + +article .row div .lb-main { + display: block; + margin-bottom: 0.75rem; + opacity: 0.2; +} + +.page-banner.lb, +.page-banner.lb:after { + border-radius: 0; + opacity: 1; + background-color: var(--seperator); +} + +footer .lb { + background: var(--footer-link); + display: inline-block; + margin-top: 0.25rem; + margin-bottom: 0.15rem; + bottom: -4px; + height: 0.625rem; +} + +.lb--large { + height:40px; + width: 200px; +} + +.lb--long { + width: 200px; +} + +.lb--longest { + width: 300px; +} + +.lb--medium { + width: 150px; +} +.lb--slim { + height: 0.675rem; +} + +main .cover picture.lb, +main .cover.haspreview > img { + display: block; + width: 100%; + height: auto; + opacity: 1; + border-radius: 0; +} + +main .cover picture.lb.nobg { + padding-top: 50%; + background: var(--seperator); + margin-bottom: 1rem; +} + +article.fullsize .row .cover { + width: 100%; +} + +article .description .lb-main { + display: block; + margin: 0 0 0.75rem; +} + +article .description fileinfo ul .lb-main { + bottom: -4px; +} + +main .cover picture.lb:after { + border-radius: 0; +} + +.lb--imgmini { + width: 400px; + height: 225px; + margin-right: 1rem; + opacity: 0.2; +} + +@media (prefers-reduced-motion), only screen and (hover: none) and (pointer: coarse), (prefers-reduced-motion) { + .lb:after { + background: transparent !important; + display: none !important; + animation: none !important; + } +} + +/* + ===================== Common ===================== +*/ + +.inside { + width: 100%; + max-width: var(--content-max-width); + display: flex; + margin: 0 auto; +} + +.inside.vertical { + flex-direction: column; +} + +.error { + background: var(--error-bg); + color: var(--error-fg); + cursor: pointer; + text-align: center; + padding: 0.5rem; +} + +.wrapper { + background: var(--alt-bg); + color: var(--alt-color); + display: flex; + justify-content: center; + align-items: center; + min-height: calc(100vh - 200px); + padding: 1rem; +} + +.filler { + flex: 2 1 auto; +} + +.wrapper .inside { + flex-direction: column; + color: var(--alt-color); + background: var(--alt-inside-bg); + border: var(--alt-inside-border); +} + +.wrapper .error { + border: 1px solid var(--error-bg); + color: var(--error-bg); + background: transparent; +} + +.notfound { + color: var(--light); +} + +@media screen and (max-width: 639px){ + main .inside { + flex-direction: column; + } + + .wrapper { + flex-direction: column; + } +} + +/* + ===================== Header ===================== +*/ + +header { + background: var(--primary-darker-bg); + color: var(--primary-darker-fg); +} + +header a, +header a:visited, +header button { + color: var(--primary-darker-link); +} + +header p { + color: var(--primary-darker-fg-light); +} + +header .title, +header .title:visited { + min-height: 100px; + padding-left: 10px; + display: flex; + align-items: center; + background: 25px center no-repeat; + background-size: auto 91px; + flex: 0 0 auto; +} + +header .logo { + background-position: -119px 0px; + width: 81px; + height: 100px; + transform: scale(0.9); + margin-right: 1rem; +} + +header .lb { + background: var(--primary-darker-fg); +} + +header .logo.lb { + background: #eaad81; +} + +header .title h1 { + font-weight: 500; + color: var(--primary-darker-fg); +} + +header aside { + flex: 2 1 auto; + display: flex; + flex-direction: column; + align-items: flex-end; + font-size: 0.8rem; + padding: 0.5rem 0.5rem; +} + +header aside a, +header aside button { + margin-left: 0.5rem; +} + +header aside p button { + margin-left: 0; +} + +/* + ===================== Nav ===================== +*/ + +nav { + background: var(--primary-bg); +} + +nav a, nav a:visited { + flex: 2 0 auto; + text-align: center; + font-weight: 300; + padding: 10px 10px 7px 10px; + border-bottom: 3px solid var(--primary-bg); + color: var(--primary-fg); +} + +nav a .lb { + background: var(--primary-fg); + display: inline-block; +} + +nav a.active { + border-bottom-color: var(--primary-link); +} + +@media screen and (max-width: 639px){ + nav { + font-size: 0.8em; + } +} + +/* + ===================== main ===================== +*/ + +main { + min-height: calc(100vh - 294px); +} + +.page-banner, +.page-banner .page-banner-real { + background-size: cover; + background-repeat: no-repeat; + background-position: center; + height: 150px; + width: 100%; + display: block; + position: relative; +} + +.page-banner-real { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.page-banner-title { + color: white; + text-align: right; + padding: 0.5rem 1rem; + font-size: 1.6rem; + flex: 2 1 auto; + text-shadow: 0 0 .3em #000; +} + +.actions { + padding: 0.5rem 1rem; + display: flex; +} + +.actions a { + margin-left: 0.375rem; +} + +main a, +main a:visited { + color: var(--link); +} + +main h5 { + padding: 0 0.5rem 0.5rem; + margin: 0 0 0.75rem; + border-bottom: 1px solid var(--seperator); + font-size: 1rem; +} + +main h2.title, +.main h2.title { + font-size: 1.4rem; + background: var(--title-bg); + color: var(--title-fg); + text-align: center; + font-weight: 400; + padding: 0.375rem; + line-height: 1.4rem; +} + +main .container { + flex: 2 1 auto; + margin: 1rem; +} + +main .cover { + position: relative; + display: block; +} + +main .cover.haspreview > img { + margin-bottom: 1rem; +} + +main .cover.haspreview picture { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +main .cover picture img { + margin-bottom: 1rem; + width: 100%; +} + +main button, +main input[type=submit] { + border: var(--button-border); + background: #ffad42; + color: #000; + align-self: center; + padding: 0.25rem 1rem; + margin: 1rem 0 2rem; +} + +@media screen and (max-width: 639px){ + main .container { + margin: 1rem 0.25rem; + } +} + +/* ************** aside ************** */ + +main aside { + padding: 0.375rem 1rem 0.5rem; + margin: 1rem; + font-size: 0.875rem; + flex: 0 0 250px; + background: var(--content-bg); + border: var(--content-border); +} + +main aside a { + display: block; +} + +main aside h5 { + margin: 0 -0.5rem 0.25rem; + font-size: 0.9em; +} + +main aside ul { + margin: 0 0 0.5rem; + padding-left: 1.5rem; +} + +main aside .asuna { + margin-top: 2rem; + width: 200px; + height: 461px; + background-position: 0 -150px; +} + +.nightmode main aside .asuna { + background-position: -200px -150px; +} + +.daymode .day { + display: block; +} + +@media screen and (max-width: 1000px){ + main aside { + flex: 0 0 200px; + } +} + +@media screen and (max-width: 639px){ + main aside { + margin: 1rem 0.25rem; + flex: 0 0 auto; + } + + main aside.frontpage { + order: 2; + } +} + + +/* ************** paginator ************** */ + +paginator { + display: flex; + justify-content: center; + width: 100%; +} + +paginator a { + color: var(--link); + cursor: pointer; +} + +paginator a, +paginator div { + display: block; + font-size: 0.8rem; + max-width: 80px; + flex-grow: 2; + text-align: center; + padding: 0.5rem; + margin-top: 1rem; +} + +/* ************** articleslim ************** */ + +articleslim { + display: flex; + margin-bottom: 0.75rem; + padding-right: 0.5rem; +} + +articleslim p.description { + font-size: 0.75rem; +} + +articleslim .cover { + flex: 0 0 124px; + margin-right: 0.75rem; +} + +articleslim .cover picture img, +articleslim .cover.haspreview > img { + margin-bottom: 0; +} + +articleslim a.nobg { + height: 70px; + background: var(--seperator); + display: block; +} + +articleslim a.title { + display: block; + margin-bottom: 0.375rem; +} + +/* ************** article ************** */ + +article { + background: var(--content-bg); + border: var(--content-border); + margin-bottom: 1rem; +} + +article .row { + margin: 1rem 0; + display: flex; +} + +article .cover { + flex: 0 0 auto; + margin-right: 1rem; + align-self: flex-start; +} + +article a.title { + flex: 0 0 100%; + margin-bottom: 0.5rem; +} + +article .description { + font-size: 0.875rem; + margin-bottom: 1rem; + padding: 0 0.25rem; +} + +article .meta { + font-size: 0.625rem; + line-height: 0.75rem; + color: var(--light); + font-weight: 500; + padding: 1.25rem 0.25rem 0; +} + +article:not(.fullsize) .cover.haspreview > img { + width: 400px; +} + +article.fullsize .row { + margin: 1rem; + flex-direction: column; +} + +article.fullsize .cover { + margin-right: 0; +} + +@media screen and (max-width: 1000px){ + article:not(.fullsize) .cover.haspreview > img { + width: calc(100vw - 265px); + } + + article .row { + flex-direction: column; + } + + article.fullsize .row { + margin: 1rem 0.25rem; + } + + article .cover { + margin-right: 0; + } +} + +@media screen and (max-width: 639px){ + article:not(.fullsize) .cover.haspreview > img { + width: calc(100vw - 10px); + } +} + +/* ************** fileinfo ************** */ + +fileinfo { + padding-left: 0.25rem; + margin-bottom: 0.5rem; + color: var(--light); + line-height: 1rem; + font-size: 0.75rem; + display: block; + position: relative; +} + +fileinfo.slim { + padding: 0; + margin: 0; +} + +fileinfo p span, +fileinfo p a { + margin-right: 0.25rem; +} + +fileinfo p a { + font-weight: 550; + padding-right: 0.25rem; + border-right: 1px solid var(--seperator); + display: inline-block; +} + +fileinfo p span { + font-weight: 700; +} + +fileinfo .trimmed { + padding: 0.25rem 0 0.25rem 1rem; +} + +fileinfo ul { + margin: 0.5rem 0; + padding-left: 1.5rem; +} + +/* + ===================== login ===================== +*/ + +.login--first { + flex: 0 0 170px; +} + +.login { + align-items: center; + font-size: 1rem; + padding: 1rem 1rem 2rem; + margin: 1rem; + max-width: 400px; +} + +.login .title { + font-size: 1.4rem; + font-weight: 200; + margin-bottom: 2rem; + text-align: center; +} + +.login input, +.login label { + width: 100%; + max-width: 300px; +} + +.login input[type=submit] { + min-width: 150px; + margin-top: 1rem; +} + +.login--asuna { + flex: 0 0 auto; + width: 180px; + height: 494px; + background-position: -400px 0; +} + +.nightmode .login--asuna { + background-position: -580px 0; +} + + +@media screen and (max-width: 1000px){ + .login--first { + display: none; + } +} + + +@media screen and (max-width: 639px){ + .login { + order: 2; + } + + .login--asuna { + max-width: 120px; + } +} + +/* + ===================== content ===================== +*/ + +.content :is(h1, h2, h3, h4, h5, ul, ol, blockquote, p) { + margin: 0 0 0.75rem; +} + +.content :is(h1, h2, h3, h4, h5) { + padding: 0 0.5rem 0.5rem; + border-bottom: 1px solid var(--seperator); +} + +.content :is(blockquote, pre) { + background: var(--bg-content-alt); + padding: 0.5rem; +} + +.content blockquote p { + margin: 0; +} + +/* + ===================== footer ===================== +*/ + +footer { + background: var(--footer-bg); + color: var(--footer-color); + min-height: 150px; + text-align: center; + padding: 1rem; + display: flex; + align-items: center; + font-weight: 500; + font-size: 0.625rem; +} + +footer .first { + flex: 0 0 119px; +} + +footer .middle { + display: flex; + flex-direction: column; + align-items: center; + flex: 2 1 auto; + padding: 0 2rem; +} + +footer .asuna { + flex: 0 0 119px; + height: 150px; + width: 119px; + background-position: 0px 0px; +} + +footer ul { + margin: 0 0 0.25rem; + padding: 0 0 0.25rem; + border-bottom: 1px solid var(--footer-seperator); + display: flex; + justify-content: center; + flex-wrap: wrap; + min-width: 300px; +} + +footer ul li { + padding: 0 0.25rem; + list-style-position: inside; +} + +footer a { + color: var(--footer-link); + margin: 0 0 0.25rem; +} + + +@media screen and (max-width: 1000px){ + footer .first { + display: none; + } +} + + +@media screen and (max-width: 639px){ + footer{ + flex-direction: column; + } + + footer .middle { + padding: 0 0 2rem; + } + + footer .asuna { + flex: 0 0 150px; + } +} + +/* + ===================== 404 page ===================== +*/ + +.not_found { + flex-direction: column; + text-align: center; +} + +.not_found .asuna { + width: 120px; + height: 444px; + margin: 2rem 0 0rem; + background-position: -760px 0; +} + +.nightmode .not_found .asuna { + background-position: -880px 0; +} diff --git a/nfp_moe/public/assets/img/favicon.png b/nfp_moe/public/assets/img/favicon.png index d8f76d3..984f71e 100644 Binary files a/nfp_moe/public/assets/img/favicon.png and b/nfp_moe/public/assets/img/favicon.png differ diff --git a/nfp_moe/public/assets/img/heart.xcf b/nfp_moe/public/assets/img/heart.xcf index 4bdfb6e..5252c7a 100644 Binary files a/nfp_moe/public/assets/img/heart.xcf and b/nfp_moe/public/assets/img/heart.xcf differ diff --git a/nfp_moe/public/index.html b/nfp_moe/public/index.html index 4195280..898bf18 100644 --- a/nfp_moe/public/index.html +++ b/nfp_moe/public/index.html @@ -17,20 +17,151 @@ {{? }} - - - + + + + - -
- + +
+ {{ if (banner) { }} +
+ {{ } }} + {{ if (type === 'page' || type === 'page_with_children') { }} +
+
+

+
+ {{ } }} +
+ {{ if (type === 'page_with_children' || type === 'frontpage') { }} + + {{ } }} + {{ if (type === 'frontpage') { }} +
+
+

+
+
+
+
 
+
+
+
+
+
+
+
+
+
+

+
+
+
+
 
+
+
+
+
+
+
+
+
+
+ {{ } else if (type === 'page' || type === 'page_with_children') { }} +
+ {{ if (media) { }} +
+ {{ } }} +
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+ + +
+
+
+
+
+
+ {{ } else if (type === 'article') { }} +
+ +
+

+
+ {{ if (media) { }} +
+ {{ } }} +
+
+
+
+
+
+

 

+ +
+
    +
  • +
  • +
+
+
+
+
+ {{ } }} +
+
+