Merge branch 'nuke-scss' of git.handmade.network:hmn/hmn into nuke-scss

This commit is contained in:
Asaf Gartner 2024-06-25 22:50:51 +03:00
commit 410c94bb51
37 changed files with 324 additions and 986 deletions

View File

@ -375,7 +375,7 @@ function editTimelineSnippet(timelineItemEl, stickyProjectId) {
let ownerAvatar = timelineItemEl.querySelector(".avatar")?.src; let ownerAvatar = timelineItemEl.querySelector(".avatar")?.src;
let creationDate = new Date(timelineItemEl.querySelector("time").dateTime); let creationDate = new Date(timelineItemEl.querySelector("time").dateTime);
let rawDesc = timelineItemEl.querySelector(".rawdesc").textContent; let rawDesc = timelineItemEl.querySelector(".rawdesc").textContent;
let attachment = timelineItemEl.querySelector(".timeline-content-box")?.children?.[0]; let attachment = timelineItemEl.querySelector(".timeline-media")?.children?.[0];
let projectIds = []; let projectIds = [];
let projectEls = timelineItemEl.querySelectorAll(".projects > a"); let projectEls = timelineItemEl.querySelectorAll(".projects > a");
for (let i = 0; i < projectEls.length; ++i) { for (let i = 0; i < projectEls.length; ++i) {

View File

@ -1,106 +1,24 @@
function TabState(tabbed) { function initTabs(container, initialTab = null) {
this.container = tabbed; const buttons = Array.from(container.querySelectorAll("[data-tab-button]"));
this.tabs = tabbed.querySelector(".tab"); const tabs = Array.from(container.querySelectorAll("[data-tab]"));
this.tabbar = document.createElement("div"); if (!initialTab) {
this.tabbar.classList.add("tab-bar"); initialTab = tabs[0].getAttribute("data-tab");
this.container.insertBefore(this.tabbar, this.container.firstChild);
this.current_i = -1;
this.tab_buttons = [];
}
function switch_tab_old(state, tab_i) {
return function() {
if (state.current_i >= 0) {
state.tabs[state.current_i].classList.add("hidden");
state.tab_buttons[state.current_i].classList.remove("current");
} }
state.tabs[tab_i].classList.remove("hidden"); function switchTo(name) {
state.tab_buttons[tab_i].classList.add("current");
var hash = "";
if (state.tabs[tab_i].hasAttribute("data-url-hash")) {
hash = state.tabs[tab_i].getAttribute("data-url-hash");
}
window.location.hash = hash;
state.current_i = tab_i;
};
}
document.addEventListener("DOMContentLoaded", function() {
const tabContainers = document.getElementsByClassName("tabbed");
for (const container of tabContainers) {
const tabBar = document.createElement("div");
tabBar.classList.add("tab-bar");
container.insertAdjacentElement('afterbegin', tabBar);
const tabs = container.querySelectorAll(".tab");
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.classList.toggle('dn', i > 0);
const slug = tab.getAttribute("data-slug");
// TODO: Should this element be a link?
const tabButton = document.createElement("div");
tabButton.classList.add("tab-button");
tabButton.classList.toggle("current", i === 0);
tabButton.innerText = tab.getAttribute("data-name");
tabButton.setAttribute("data-slug", slug);
tabButton.addEventListener("click", () => {
switchTab(container, slug);
});
tabBar.appendChild(tabButton);
}
const initialSlug = window.location.hash;
if (initialSlug) {
switchTab(container, initialSlug.substring(1));
}
}
});
function switchTab(container, slug) {
const tabs = container.querySelectorAll('.tab');
let didMatch = false;
for (const tab of tabs) { for (const tab of tabs) {
const slugMatches = tab.getAttribute("data-slug") === slug; tab.hidden = tab.getAttribute("data-tab") !== name;
tab.classList.toggle('dn', !slugMatches); }
for (const button of buttons) {
if (slugMatches) { button.classList.toggle("tab-button-active", button.getAttribute("data-tab-button") === name);
didMatch = true;
} }
} }
switchTo(initialTab);
const tabButtons = document.querySelectorAll(".tab-button"); for (const button of buttons) {
for (const tabButton of tabButtons) { button.addEventListener("click", () => {
const buttonSlug = tabButton.getAttribute("data-slug"); switchTo(button.getAttribute("data-tab-button"));
tabButton.classList.toggle('current', slug === buttonSlug); });
}
if (!didMatch) {
// switch to first tab as a fallback
tabs[0].classList.remove('dn');
tabButtons[0].classList.add('current');
}
window.location.hash = slug;
}
function switchToTabOfElement(container, el) {
const tabs = Array.from(container.querySelectorAll('.tab'));
let target = el.parentElement;
while (target) {
if (tabs.includes(target)) {
switchTab(container, target.getAttribute("data-slug"));
return;
}
target = target.parentElement;
} }
} }

View File

@ -7159,9 +7159,6 @@ code {
--color: black; --color: black;
--link-color: #d12991; --link-color: #d12991;
--red: #c61d24; --red: #c61d24;
--dim-color: #333;
--dimmer-color: #999;
--dimmest-color: #bbb;
--theme-color: #b1b1b1; --theme-color: #b1b1b1;
--theme-color-dim: #c0c0c0; --theme-color-dim: #c0c0c0;
--theme-color-dimmer: #dddddd; --theme-color-dimmer: #dddddd;
@ -7173,6 +7170,7 @@ code {
--card-background: #ebebeb; --card-background: #ebebeb;
--card-background-hover: #f1f1f1; --card-background-hover: #f1f1f1;
--card-background-transparent: #ebebeb00; --card-background-transparent: #ebebeb00;
--timeline-media-background: #b4b4b466;
--bg-1: #f8f8f8; --bg-1: #f8f8f8;
--bg-2: #e8e8e8; --bg-2: #e8e8e8;
--bg-3: #d8d8d8; --bg-3: #d8d8d8;
@ -7200,9 +7198,6 @@ code {
--color: #eee; --color: #eee;
--link-color: #ff5dc2; --link-color: #ff5dc2;
--color-error: #ff6666; --color-error: #ff6666;
--dim-color: #bbb;
--dimmer-color: #999;
--dimmest-color: #777;
--theme-color: #666; --theme-color: #666;
--theme-color-dim: #444; --theme-color-dim: #444;
--theme-color-dimmer: #383838; --theme-color-dimmer: #383838;
@ -7214,6 +7209,7 @@ code {
--card-background: #494949; --card-background: #494949;
--card-background-hover: #333; --card-background-hover: #333;
--card-background-transparent: #24242400; --card-background-transparent: #24242400;
--timeline-media-background: #24242466;
--bg-1: #1f1f1f; --bg-1: #1f1f1f;
--bg-2: #2f2f2f; --bg-2: #2f2f2f;
--bg-3: #494949; --bg-3: #494949;
@ -7323,27 +7319,6 @@ pre,
flex-shrink: 1; flex-shrink: 1;
} }
} }
.b--dim {
border-color: var(--dim-color);
}
.b--dimmer {
border-color: var(--dimmer-color);
}
.b--dimmest {
border-color: var(--dimmest-color);
}
.b--theme {
border-color: var(--theme-color);
}
.b--theme-dim {
border-color: var(--theme-color-dim);
}
.b--theme-dimmer {
border-color: var(--theme-color-dimmer);
}
.b--theme-dimmest {
border-color: var(--theme-color-dimmest);
}
.b--theme-dark { .b--theme-dark {
border-color: var(--theme-color-dark); border-color: var(--theme-color-dark);
} }
@ -7393,24 +7368,6 @@ pre,
.c--inherit:active { .c--inherit:active {
color: inherit; color: inherit;
} }
.c--dim {
color: var(--dim-color);
}
.c--theme-dim {
color: var(--theme-color-dim);
}
.c--dimmer {
color: var(--dimmer-color);
}
.c--theme-dimmer {
color: var(--theme-color-dimmer);
}
.c--dimmest {
color: var(--dimmest-color);
}
.c--theme-dimmest {
color: var(--theme-color-dimmest);
}
.f8 { .f8 {
font-size: 0.65rem; font-size: 0.65rem;
} }
@ -7809,7 +7766,7 @@ pre,
align-items: center; align-items: center;
border-style: dashed; border-style: dashed;
border-width: 0 0 1px; border-width: 0 0 1px;
border-color: var(--dimmest-color); border-color: var(--bg-3);
} }
@media screen and (min-width: 35em) { @media screen and (min-width: 35em) {
.optionbar { .optionbar {
@ -7885,7 +7842,7 @@ pre,
transition: all 100ms ease-in-out; transition: all 100ms ease-in-out;
} }
.carousel-container .carousel-button:hover { .carousel-container .carousel-button:hover {
background-color: var(--dimmest-color); background-color: var(--bg-3);
} }
.carousel-container .carousel-button.active { .carousel-container .carousel-button.active {
border-color: var(--theme-color); border-color: var(--theme-color);
@ -7967,10 +7924,10 @@ pre,
.post-content th, .post-content th,
.post-content td { .post-content td {
padding: var(--spacing-extra-small) var(--spacing-small); padding: var(--spacing-extra-small) var(--spacing-small);
border: 1px solid var(--dimmest-color); border: 1px solid var(--border-color);
} }
.post-content code { .post-content code {
background-color: var(--dim-background); background-color: var(--bg-3);
padding: .2em 0; padding: .2em 0;
white-space: nowrap; white-space: nowrap;
} }
@ -7982,12 +7939,12 @@ pre,
} }
.post-content pre > code, .post-content pre > code,
.post-content pre.hmn-code { .post-content pre.hmn-code {
background-color: var(--dim-background); background-color: var(--bg-3);
padding: 0.7em; padding: 0.7em;
overflow-x: auto; overflow-x: auto;
} }
.post-content blockquote { .post-content blockquote {
border-color: var(--dimmest-color); border-color: var(--bg-3);
margin-left: var(--spacing-small); margin-left: var(--spacing-small);
padding-left: var(--spacing-small); padding-left: var(--spacing-small);
margin-right: 0; margin-right: 0;
@ -8130,7 +8087,7 @@ pre,
height: var(--height); height: var(--height);
border-width: 0 0 1px 1px; border-width: 0 0 1px 1px;
border-style: solid; border-style: solid;
border-color: var(--dimmest-color); border-color: var(--bg-3);
left: -1.5rem; left: -1.5rem;
top: calc(1rem - var(--height)); top: calc(1rem - var(--height));
border-bottom-left-radius: 0.5rem; border-bottom-left-radius: 0.5rem;
@ -8471,7 +8428,7 @@ header.old .submenu > a {
} }
} }
header { header {
background-color: var(--bg-3); background-color: var(--bg-2);
border-bottom-style: solid; border-bottom-style: solid;
border-bottom-width: 1px; border-bottom-width: 1px;
} }
@ -8501,7 +8458,7 @@ header .header-nav .submenu {
position: absolute; position: absolute;
z-index: 100; z-index: 100;
min-width: 8rem; min-width: 8rem;
background-color: var(--card-background); background-color: var(--bg-2);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-top-width: 0; border-top-width: 0;
@ -8887,11 +8844,20 @@ code .ss,
color: #a31515; color: #a31515;
} }
/* src/rawdata/scss/tabs.css */
.tab-button {
border-bottom: 2px solid transparent;
margin-bottom: -1px;
}
.tab-button-active {
border-color: var(--link-color);
}
/* src/rawdata/scss/timeline.css */ /* src/rawdata/scss/timeline.css */
.avatar { .avatar {
object-fit: cover; object-fit: cover;
overflow: hidden; overflow: hidden;
background-color: var(--dimmest-color); background-color: var(--bg-3);
flex-shrink: 0; flex-shrink: 0;
border: none; border: none;
width: var(--avatar-size-normal); width: var(--avatar-size-normal);
@ -8909,16 +8875,28 @@ code .ss,
--fade-color: var(--card-background); --fade-color: var(--card-background);
color: var(--main-color); color: var(--main-color);
} }
.timeline-item .timeline-content-box.timeline-item-bg { .timeline-item .timeline-media {
background-color: var(--timeline-content-background); background-color: var(--timeline-media-background);
max-height: 60vh;
} }
.timeline-item .timeline-content-box > * { .timeline-item .timeline-media.timeline-embed {
height: 0;
position: relative;
padding-bottom: 56.25%;
}
.timeline-item .timeline-media.timeline-embed > iframe {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
}
.timeline-item .timeline-media > * {
display: block; display: block;
max-width: 100%; max-width: 100%;
max-height: 80vh;
}
.timeline-item .avatar {
width: 2.5rem;
} }
.timeline-modal .container { .timeline-modal .container {
max-height: 100vh; max-height: 100vh;

View File

@ -1,309 +0,0 @@
/*
Inserts a CSS expression with one or more custom variables.
You can provide an arbitrary number of strings in the second
argument, separated by spaces. Any strings corresponding to
variable names will be replaced by the correct values, while
other strings are left untouched.
Example usage:
@include usevar(border-color, dimmer-color);
@include usevar(background, "linear-gradient(" dim-background-transparent "," dim-background ")");
For clarity and to avoid syntax issues, you are encouraged to
use unquoted strings for variables and quoted strings for
everything else.
For convenience in common cases, if only a single argument
is provided and it does not match an existing variable, this
will throw an error.
*/
pre, code, .codeblock {
/* Comment */
/* Error */
/* Keyword */
/* Literal */
/* Name */
/* Operator */
/* Punctuation */
/* Comment.Multiline */
/* Comment.Preproc */
/* Comment.Single */
/* Comment.Special */
/* Generic.Emph */
/* Generic.Strong */
/* Keyword.Constant */
/* Keyword.Declaration */
/* Keyword.Namespace */
/* Keyword.Pseudo */
/* Keyword.Reserved */
/* Keyword.Type */
/* Literal.Date */
/* Literal.Number */
/* Literal.String */
/* Name.Attribute */
/* Name.Builtin */
/* Name.Class */
/* Name.Constant */
/* Name.Decorator */
/* Name.Entity */
/* Name.Exception */
/* Name.Function */
/* Name.Label */
/* Name.Namespace */
/* Name.Other */
/* Name.Property */
/* Name.Tag */
/* Name.Variable */
/* Operator.Word */
/* Text.Whitespace */
/* Literal.Number.Float */
/* Literal.Number.Hex */
/* Literal.Number.Integer */
/* Literal.Number.Oct */
/* Literal.String.Backtick */
/* Literal.String.Char */
/* Literal.String.Doc */
/* Literal.String.Double */
/* Literal.String.Escape */
/* Literal.String.Heredoc */
/* Literal.String.Interpol */
/* Literal.String.Other */
/* Literal.String.Regex */
/* Literal.String.Single */
/* Literal.String.Symbol */
/* Name.Builtin.Pseudo */
/* Name.Variable.Class */
/* Name.Variable.Global */
/* Name.Variable.Instance */
/* Literal.Number.Integer.Long */
/* Generic Heading & Diff Header */
/* Generic.Subheading & Diff Unified/Comment? */
/* Generic.Deleted & Diff Deleted */
/* Generic.Inserted & Diff Inserted */ }
pre .hll, code .hll, .codeblock .hll {
background-color: #49483e; }
pre .c, code .c, .codeblock .c {
color: #75715e; }
pre .err, code .err, .codeblock .err {
color: #ff0000; }
pre .k, code .k, .codeblock .k {
color: #66d9ef; }
pre .l, code .l, .codeblock .l {
color: #ae81ff; }
pre .n, code .n, .codeblock .n {
color: #f8f8f2; }
pre .o, code .o, .codeblock .o {
color: #f92672; }
pre .p, code .p, .codeblock .p {
color: #f8f8f2; }
pre .cm, code .cm, .codeblock .cm {
color: #75715e; }
pre .cp, code .cp, .codeblock .cp {
color: #75715e; }
pre .c1, code .c1, .codeblock .c1 {
color: #75715e; }
pre .cs, code .cs, .codeblock .cs {
color: #75715e; }
pre .ge, code .ge, .codeblock .ge {
font-style: italic; }
pre .gs, code .gs, .codeblock .gs {
font-weight: bold; }
pre .kc, code .kc, .codeblock .kc {
color: #66d9ef; }
pre .kd, code .kd, .codeblock .kd {
color: #66d9ef; }
pre .kn, code .kn, .codeblock .kn {
color: #f92672; }
pre .kp, code .kp, .codeblock .kp {
color: #66d9ef; }
pre .kr, code .kr, .codeblock .kr {
color: #66d9ef; }
pre .kt, code .kt, .codeblock .kt {
color: #66d9ef; }
pre .ld, code .ld, .codeblock .ld {
color: #e6db74; }
pre .m, code .m, .codeblock .m {
color: #ae81ff; }
pre .s, code .s, .codeblock .s {
color: #e6db74; }
pre .na, code .na, .codeblock .na {
color: #a6e22e; }
pre .nb, code .nb, .codeblock .nb {
color: #f8f8f2; }
pre .nc, code .nc, .codeblock .nc {
color: #a6e22e; }
pre .no, code .no, .codeblock .no {
color: #66d9ef; }
pre .nd, code .nd, .codeblock .nd {
color: #a6e22e; }
pre .ni, code .ni, .codeblock .ni {
color: #f8f8f2; }
pre .ne, code .ne, .codeblock .ne {
color: #a6e22e; }
pre .nf, code .nf, .codeblock .nf {
color: #a6e22e; }
pre .nl, code .nl, .codeblock .nl {
color: #f8f8f2; }
pre .nn, code .nn, .codeblock .nn {
color: #f8f8f2; }
pre .nx, code .nx, .codeblock .nx {
color: #a6e22e; }
pre .py, code .py, .codeblock .py {
color: #f8f8f2; }
pre .nt, code .nt, .codeblock .nt {
color: #f92672; }
pre .nv, code .nv, .codeblock .nv {
color: #f8f8f2; }
pre .ow, code .ow, .codeblock .ow {
color: #f92672; }
pre .w, code .w, .codeblock .w {
color: #f8f8f2; }
pre .mf, code .mf, .codeblock .mf {
color: #ae81ff; }
pre .mh, code .mh, .codeblock .mh {
color: #ae81ff; }
pre .mi, code .mi, .codeblock .mi {
color: #ae81ff; }
pre .mo, code .mo, .codeblock .mo {
color: #ae81ff; }
pre .sb, code .sb, .codeblock .sb {
color: #e6db74; }
pre .sc, code .sc, .codeblock .sc {
color: #e6db74; }
pre .sd, code .sd, .codeblock .sd {
color: #e6db74; }
pre .s2, code .s2, .codeblock .s2 {
color: #e6db74; }
pre .se, code .se, .codeblock .se {
color: #ae81ff; }
pre .sh, code .sh, .codeblock .sh {
color: #e6db74; }
pre .si, code .si, .codeblock .si {
color: #e6db74; }
pre .sx, code .sx, .codeblock .sx {
color: #e6db74; }
pre .sr, code .sr, .codeblock .sr {
color: #e6db74; }
pre .s1, code .s1, .codeblock .s1 {
color: #e6db74; }
pre .ss, code .ss, .codeblock .ss {
color: #e6db74; }
pre .bp, code .bp, .codeblock .bp {
color: #f8f8f2; }
pre .vc, code .vc, .codeblock .vc {
color: #f8f8f2; }
pre .vg, code .vg, .codeblock .vg {
color: #f8f8f2; }
pre .vi, code .vi, .codeblock .vi {
color: #f8f8f2; }
pre .il, code .il, .codeblock .il {
color: #ae81ff; }
pre .gu, code .gu, .codeblock .gu {
color: #75715e; }
pre .gd, code .gd, .codeblock .gd {
color: #f92672; }
pre .gi, code .gi, .codeblock .gi {
color: #a6e22e; }
.light {
background-color: #fff;
color: #000; }
:root {
--fg-font-color: #eee;
--theme-color: #666;
--theme-color-dim: #444;
--theme-color-dimmer: #383838;
--theme-color-dimmest: #333;
--theme-color-dark: #666;
--theme-color-light: #666;
--link-color: #aaa;
--link-border-color: #aaa;
--hr-color: #aaa;
--main-background-color: #202020;
--main-color: #eee;
--dim-color: #bbb;
--dimmer-color: #999;
--dimmest-color: #777;
--menu-bottom-border-color: #444;
--login-popup-background: #181818;
--content-background: #202020;
--content-background-transparent: rgba(32, 32, 32, 0);
--dim-background: #252525;
--dim-background-transparent: rgba(37, 37, 37, 0);
--text-background: #181818;
--spoiler-border: #777;
--background-even-background: #242424;
--project-card-border-color: #333;
--project-user-suggestions-background: #222;
--project-user-suggestions-border-color: #444;
--notice-text-color: #eee;
--notice-unapproved-color: #7a2020;
--notice-hidden-color: #494949;
--notice-hiatus-color: #876327;
--notice-dead-color: #7a2020;
--notice-lts-color: #2a681d;
--notice-lts-reqd-color: #876327;
--notice-success-color: #2a681d;
--notice-warn-color: #876327;
--notice-failure-color: #7a2020;
--optionbar-border-color: #333;
--tab-background: #181818;
--tab-border-color: #3f3f3f;
--tab-button-background: #303030;
--tab-button-background-hover: #383838;
--tab-button-background-current: #181818;
--form-check-background: #252527;
--form-check-border-color: #666;
--form-check-border-color-hover: #084068;
--form-text-background: #181818;
--form-text-background-active: #252527;
--form-text-border-color: #444;
--form-text-border-color-active: #084068;
--form-button-color: #999;
--form-button-color-active: #4c9ed9;
--form-button-background: #383838;
--form-button-background-active: #303840;
--form-button-border-color: transparent;
--form-button-inline-border-color: transparent;
--form-error-color: #c61d24;
--landing-search-background: #282828;
--landing-search-background-hover: #181818;
--editor-toolbar-background: #282828;
--editor-toolbar-border-color: #333;
--editor-toolbar-button-background: #282828;
--editor-toolbar-button-background-hover: #333;
--editor-toolbar-button-border-color: #333;
--post-blockquote-border-color: #555;
--forum-even-background: #242424;
--forum-thread-read-color: #777;
--forum-thread-read-link-color: #999;
--forum-post-author-color: #999;
--forum-diff-source-background: #181818;
--forum-diff-source-border-color: #444;
--forum-diff-replace-background: #18283a;
--forum-diff-replace-border-color: #223d5b;
--forum-diff-delete-background: #3a1818;
--forum-diff-delete-border-color: #6b1e1c;
--forum-diff-insert-background: #233a18;
--forum-diff-insert-border-color: #30591b;
--card-background: #282828;
--card-background-hover: #333;
--timeline-content-background: rgba(255, 255, 255, 0.06);
--irc-border-color: #333;
--irc-tab-current-shadow: 0px 0px 5px #000 inset;
--irc-tab-close-button-color: #bbb;
--irc-tab-close-button-background: #444;
--irc-nick-border-color: #444;
--irc-users-color: #aaa;
--irc-users-background: #181818;
--irc-users-border-color: transparent;
--irc-users-popout-background: #181818;
--irc-users-popout-border-color-left: #444;
--irc-users-popout-border-color-right: #333;
--code-line-number-color: #444;
--library-star-btn-background: #252525;
--library-star-btn-border-color: #bbb;
--library-star-btn-a-border-color: #999;
--library-star-btn-a-hover-background: #333; }

View File

@ -1,327 +0,0 @@
/*
Inserts a CSS expression with one or more custom variables.
You can provide an arbitrary number of strings in the second
argument, separated by spaces. Any strings corresponding to
variable names will be replaced by the correct values, while
other strings are left untouched.
Example usage:
@include usevar(border-color, dimmer-color);
@include usevar(background, "linear-gradient(" dim-background-transparent "," dim-background ")");
For clarity and to avoid syntax issues, you are encouraged to
use unquoted strings for variables and quoted strings for
everything else.
For convenience in common cases, if only a single argument
is provided and it does not match an existing variable, this
will throw an error.
*/
pre, code, .codeblock {
/* Comment */
/* Error */
/* Keyword */
/* Operator */
/* Comment.Multiline */
/* Comment.Preproc */
/* Comment.Single */
/* Comment.Special */
/* Generic.Deleted */
/* Generic.Emph */
/* Generic.Error */
/* Generic.Heading */
/* Generic.Inserted */
/* Generic.Output */
/* Generic.Prompt */
/* Generic.Strong */
/* Generic.Subheading */
/* Generic.Traceback */
/* Keyword.Constant */
/* Keyword.Declaration */
/* Keyword.Namespace */
/* Keyword.Pseudo */
/* Keyword.Reserved */
/* Keyword.Type */
/* Literal.Number */
/* Literal.String */
/* Name.Attribute */
/* Name.Builtin */
/* Name.Class */
/* Name.Constant */
/* Name.Decorator */
/* Name.Entity */
/* Name.Exception */
/* Name.Function */
/* Name.Label */
/* Name.Namespace */
/* Name.Tag */
/* Name.Variable */
/* Operator.Word */
/* Text.Whitespace */
/* Literal.Number.Float */
/* Literal.Number.Hex */
/* Literal.Number.Integer */
/* Literal.Number.Oct */
/* Literal.String.Backtick */
/* Literal.String.Char */
/* Literal.String.Doc */
/* Literal.String.Double */
/* Literal.String.Escape */
/* Literal.String.Heredoc */
/* Literal.String.Interpol */
/* Literal.String.Other */
/* Literal.String.Regex */
/* Literal.String.Single */
/* Literal.String.Symbol */
/* Name.Builtin.Pseudo */
/* Name.Variable.Class */
/* Name.Variable.Global */
/* Name.Variable.Instance */
/* Literal.Number.Integer.Long */ }
pre .hll, code .hll, .codeblock .hll {
background-color: #ffffcc; }
pre .c, code .c, .codeblock .c {
color: #60a0b0;
font-style: italic; }
pre .err, code .err, .codeblock .err {
color: #FF0000; }
pre .k, code .k, .codeblock .k {
color: #007020;
font-weight: bold; }
pre .o, code .o, .codeblock .o {
color: #666666; }
pre .cm, code .cm, .codeblock .cm {
color: #60a0b0;
font-style: italic; }
pre .cp, code .cp, .codeblock .cp {
color: #007020; }
pre .c1, code .c1, .codeblock .c1 {
color: #60a0b0;
font-style: italic; }
pre .cs, code .cs, .codeblock .cs {
color: #60a0b0;
background-color: #fff0f0; }
pre .gd, code .gd, .codeblock .gd {
color: #A00000; }
pre .ge, code .ge, .codeblock .ge {
font-style: italic; }
pre .gr, code .gr, .codeblock .gr {
color: #FF0000; }
pre .gh, code .gh, .codeblock .gh {
color: #000080;
font-weight: bold; }
pre .gi, code .gi, .codeblock .gi {
color: #00A000; }
pre .go, code .go, .codeblock .go {
color: #808080; }
pre .gp, code .gp, .codeblock .gp {
color: #c65d09;
font-weight: bold; }
pre .gs, code .gs, .codeblock .gs {
font-weight: bold; }
pre .gu, code .gu, .codeblock .gu {
color: #800080;
font-weight: bold; }
pre .gt, code .gt, .codeblock .gt {
color: #0040D0; }
pre .kc, code .kc, .codeblock .kc {
color: #007020;
font-weight: bold; }
pre .kd, code .kd, .codeblock .kd {
color: #007020;
font-weight: bold; }
pre .kn, code .kn, .codeblock .kn {
color: #007020;
font-weight: bold; }
pre .kp, code .kp, .codeblock .kp {
color: #007020; }
pre .kr, code .kr, .codeblock .kr {
color: #007020;
font-weight: bold; }
pre .kt, code .kt, .codeblock .kt {
color: #902000; }
pre .m, code .m, .codeblock .m {
color: #40a070; }
pre .s, code .s, .codeblock .s {
color: #4070a0; }
pre .na, code .na, .codeblock .na {
color: #4070a0; }
pre .nb, code .nb, .codeblock .nb {
color: #007020; }
pre .nc, code .nc, .codeblock .nc {
color: #0e84b5;
font-weight: bold; }
pre .no, code .no, .codeblock .no {
color: #60add5; }
pre .nd, code .nd, .codeblock .nd {
color: #555555;
font-weight: bold; }
pre .ni, code .ni, .codeblock .ni {
color: #d55537;
font-weight: bold; }
pre .ne, code .ne, .codeblock .ne {
color: #007020; }
pre .nf, code .nf, .codeblock .nf {
color: #06287e; }
pre .nl, code .nl, .codeblock .nl {
color: #002070;
font-weight: bold; }
pre .nn, code .nn, .codeblock .nn {
color: #0e84b5;
font-weight: bold; }
pre .nt, code .nt, .codeblock .nt {
color: #062873;
font-weight: bold; }
pre .nv, code .nv, .codeblock .nv {
color: #bb60d5; }
pre .ow, code .ow, .codeblock .ow {
color: #007020;
font-weight: bold; }
pre .w, code .w, .codeblock .w {
color: #bbbbbb; }
pre .mf, code .mf, .codeblock .mf {
color: #40a070; }
pre .mh, code .mh, .codeblock .mh {
color: #40a070; }
pre .mi, code .mi, .codeblock .mi {
color: #40a070; }
pre .mo, code .mo, .codeblock .mo {
color: #40a070; }
pre .sb, code .sb, .codeblock .sb {
color: #4070a0; }
pre .sc, code .sc, .codeblock .sc {
color: #4070a0; }
pre .sd, code .sd, .codeblock .sd {
color: #4070a0;
font-style: italic; }
pre .s2, code .s2, .codeblock .s2 {
color: #4070a0; }
pre .se, code .se, .codeblock .se {
color: #4070a0;
font-weight: bold; }
pre .sh, code .sh, .codeblock .sh {
color: #4070a0; }
pre .si, code .si, .codeblock .si {
color: #70a0d0;
font-style: italic; }
pre .sx, code .sx, .codeblock .sx {
color: #c65d09; }
pre .sr, code .sr, .codeblock .sr {
color: #235388; }
pre .s1, code .s1, .codeblock .s1 {
color: #4070a0; }
pre .ss, code .ss, .codeblock .ss {
color: #517918; }
pre .bp, code .bp, .codeblock .bp {
color: #007020; }
pre .vc, code .vc, .codeblock .vc {
color: #bb60d5; }
pre .vg, code .vg, .codeblock .vg {
color: #bb60d5; }
pre .vi, code .vi, .codeblock .vi {
color: #bb60d5; }
pre .il, code .il, .codeblock .il {
color: #40a070; }
.dark {
background-color: #222;
color: #bbb; }
:root {
--fg-font-color: black;
--theme-color: #666;
--theme-color-dim: #aaa;
--theme-color-dimmer: #bbb;
--theme-color-dimmest: #ccc;
--theme-color-dark: #666;
--theme-color-light: #666;
--link-color: #666;
--link-border-color: #666;
--hr-color: #444;
--main-background-color: #fff;
--main-color: black;
--dim-color: #333;
--dimmer-color: #999;
--dimmest-color: #bbb;
--menu-bottom-border-color: black;
--login-popup-background: #fbfbfb;
--content-background: #f8f8f8;
--content-background-transparent: rgba(248, 248, 248, 0);
--dim-background: #f0f0f0;
--dim-background-transparent: rgba(240, 240, 240, 0);
--text-background: #f9f9f9;
--spoiler-border: #aaa;
--background-even-background: #f8f8f8;
--project-card-border-color: #aaa;
--project-user-suggestions-background: #fff;
--project-user-suggestions-border-color: #ddd;
--notice-text-color: #fff;
--notice-unapproved-color: #b42222;
--notice-hidden-color: #b6b6b6;
--notice-hiatus-color: #aa7d30;
--notice-dead-color: #b42222;
--notice-lts-color: #43a52f;
--notice-lts-reqd-color: #aa7d30;
--notice-success-color: #43a52f;
--notice-warn-color: #aa7d30;
--notice-failure-color: #b42222;
--optionbar-border-color: #ccc;
--tab-background: #fff;
--tab-border-color: #d8d8d8;
--tab-button-background: #dfdfdf;
--tab-button-background-hover: #efefef;
--tab-button-background-current: #fff;
--form-check-background: #fafafc;
--form-check-border-color: #999;
--form-check-border-color-hover: #4c9ed9;
--form-text-background: #fff;
--form-text-background-active: #fafafc;
--form-text-border-color: #999;
--form-text-border-color-active: #4c9ed9;
--form-button-color: black;
--form-button-color-active: #4c9ed9;
--form-button-background: #fff;
--form-button-background-active: #f2f2f2;
--form-button-border-color: #ccc;
--form-button-inline-border-color: #999;
--form-error-color: #c61d24;
--landing-search-background: #f8f8f8;
--landing-search-background-hover: #fefeff;
--editor-toolbar-background: #fff;
--editor-toolbar-border-color: transparent;
--editor-toolbar-button-background: transparent;
--editor-toolbar-button-background-hover: #ddd;
--editor-toolbar-button-border-color: #ccc;
--post-blockquote-border-color: #ddd;
--forum-even-background: #f0f0f0;
--forum-thread-read-color: #555;
--forum-thread-read-link-color: #888;
--forum-post-author-color: #333;
--forum-diff-source-background: #fff;
--forum-diff-source-border-color: #999;
--forum-diff-replace-background: #adcef4;
--forum-diff-replace-border-color: #4787d1;
--forum-diff-delete-background: #e57979;
--forum-diff-delete-border-color: #c12626;
--forum-diff-insert-background: #96e579;
--forum-diff-insert-border-color: #5baa3f;
--card-background: #e8e8e8;
--card-background-hover: #f0f0f0;
--timeline-content-background: rgba(0, 0, 0, 0.2);
--irc-border-color: #ddd;
--irc-tab-current-shadow: 0px 0px 5px #bbb inset;
--irc-tab-close-button-color: #fff;
--irc-tab-close-button-background: #aaa;
--irc-nick-border-color: #ccc;
--irc-users-color: black;
--irc-users-background: #fff;
--irc-users-border-color: #ccc;
--irc-users-popout-background: #fff;
--irc-users-popout-border-color-left: #bbb;
--irc-users-popout-border-color-right: #ccc;
--code-line-number-color: #777;
--library-star-btn-background: #fff;
--library-star-btn-border-color: #999;
--library-star-btn-a-border-color: #aaa;
--library-star-btn-a-hover-background: #fafafa; }

View File

@ -23,6 +23,7 @@ type ProjectsQuery struct {
// are generally visible to all users. // are generally visible to all users.
Lifecycles []models.ProjectLifecycle // If empty, defaults to visible lifecycles. Do not conflate this with permissions; those are checked separately. Lifecycles []models.ProjectLifecycle // If empty, defaults to visible lifecycles. Do not conflate this with permissions; those are checked separately.
Types ProjectTypeQuery // bitfield Types ProjectTypeQuery // bitfield
FeaturedOnly bool
IncludeHidden bool IncludeHidden bool
// Ignored when using FetchProject // Ignored when using FetchProject
@ -133,6 +134,9 @@ func FetchProjects(
} }
qb.Add(`)`) qb.Add(`)`)
} }
if q.FeaturedOnly {
qb.Add(`AND project.featured`)
}
if !q.IncludeHidden { if !q.IncludeHidden {
qb.Add(`AND NOT project.hidden`) qb.Add(`AND NOT project.hidden`)
} }

View File

@ -16,6 +16,8 @@ type SnippetQuery struct {
Tags []int Tags []int
DiscordMessageIDs []string DiscordMessageIDs []string
FeaturedOnly bool
Limit, Offset int // if empty, no pagination Limit, Offset int // if empty, no pagination
} }

View File

@ -20,7 +20,7 @@
transition: all 100ms ease-in-out; transition: all 100ms ease-in-out;
&:hover { &:hover {
background-color: var(--dimmest-color); background-color: var(--bg-3);
} }
&.active { &.active {

View File

@ -87,11 +87,11 @@
th, th,
td { td {
padding: var(--spacing-extra-small) var(--spacing-small); padding: var(--spacing-extra-small) var(--spacing-small);
border: 1px solid var(--dimmest-color); border: 1px solid var(--border-color);
} }
code { code {
background-color: var(--dim-background); background-color: var(--bg-3);
padding: .2em 0; padding: .2em 0;
white-space: nowrap; white-space: nowrap;
@ -106,14 +106,14 @@
pre>code, pre>code,
pre.hmn-code { pre.hmn-code {
background-color: var(--dim-background); background-color: var(--bg-3);
padding: 0.7em; padding: 0.7em;
overflow-x: auto; overflow-x: auto;
} }
blockquote { blockquote {
border-color: var(--dimmest-color); border-color: var(--bg-3);
margin-left: var(--spacing-small); margin-left: var(--spacing-small);
padding-left: var(--spacing-small); padding-left: var(--spacing-small);
margin-right: 0; margin-right: 0;

View File

@ -108,34 +108,6 @@ pre,
} }
} }
.b--dim {
border-color: var(--dim-color);
}
.b--dimmer {
border-color: var(--dimmer-color);
}
.b--dimmest {
border-color: var(--dimmest-color);
}
.b--theme {
border-color: var(--theme-color);
}
.b--theme-dim {
border-color: var(--theme-color-dim);
}
.b--theme-dimmer {
border-color: var(--theme-color-dimmer);
}
.b--theme-dimmest {
border-color: var(--theme-color-dimmest);
}
.b--theme-dark { .b--theme-dark {
border-color: var(--theme-color-dark); border-color: var(--theme-color-dark);
} }
@ -201,30 +173,6 @@ pre,
} }
} }
.c--dim {
color: var(--dim-color);
}
.c--theme-dim {
color: var(--theme-color-dim);
}
.c--dimmer {
color: var(--dimmer-color);
}
.c--theme-dimmer {
color: var(--theme-color-dimmer);
}
.c--dimmest {
color: var(--dimmest-color);
}
.c--theme-dimmest {
color: var(--theme-color-dimmest);
}
.f8 { .f8 {
font-size: 0.65rem; font-size: 0.65rem;
} }
@ -755,7 +703,7 @@ lite variant instead.
align-items: center; align-items: center;
border-style: dashed; border-style: dashed;
border-width: 0 0 1px; border-width: 0 0 1px;
border-color: var(--dimmest-color); border-color: var(--bg-3);
@media screen and (min-width: 35em) { @media screen and (min-width: 35em) {
flex-direction: row; flex-direction: row;

View File

@ -18,7 +18,7 @@
height: var(--height); height: var(--height);
border-width: 0 0 1px 1px; border-width: 0 0 1px 1px;
border-style: solid; border-style: solid;
border-color: var(--dimmest-color); border-color: var(--bg-3);
left: -1.5rem; left: -1.5rem;
top: calc(1rem - var(--height)); top: calc(1rem - var(--height));
border-bottom-left-radius: 0.5rem; border-bottom-left-radius: 0.5rem;

View File

@ -114,7 +114,7 @@ header.old {
} }
header { header {
background-color: var(--bg-3); background-color: var(--bg-2);
border-bottom-style: solid; border-bottom-style: solid;
border-bottom-width: 1px; border-bottom-width: 1px;
@ -151,7 +151,7 @@ header {
position: absolute; position: absolute;
z-index: 100; z-index: 100;
min-width: 8rem; min-width: 8rem;
background-color: var(--card-background); background-color: var(--bg-2);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-top-width: 0; border-top-width: 0;

View File

@ -16,4 +16,5 @@
@import "projects.css"; @import "projects.css";
@import "showcase.css"; @import "showcase.css";
@import "syntax.css"; @import "syntax.css";
@import "tabs.css";
@import "timeline.css"; @import "timeline.css";

View File

@ -0,0 +1,8 @@
.tab-button {
border-bottom: 2px solid transparent;
margin-bottom: -1px;
}
.tab-button-active {
border-color: var(--link-color);
}

View File

@ -1,7 +1,7 @@
.avatar { .avatar {
object-fit: cover; object-fit: cover;
overflow: hidden; overflow: hidden;
background-color: var(--dimmest-color); background-color: var(--bg-3);
flex-shrink: 0; flex-shrink: 0;
border: none; border: none;
@ -23,22 +23,34 @@
--fade-color: var(--card-background); --fade-color: var(--card-background);
color: var(--main-color); color: var(--main-color);
.timeline-content-box { .timeline-media {
&.timeline-item-bg { background-color: var(--timeline-media-background);
background-color: var(--timeline-content-background); max-height: 60vh;
&.timeline-embed {
/* aspect-ratio aspect-ratio--16x9 */
height: 0;
position: relative;
padding-bottom: 56.25%;
>iframe {
/* aspect-ratio--object */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100;
}
} }
>* { >* {
display: block; display: block;
max-width: 100%; max-width: 100%;
max-height: 80vh;
} }
} }
.avatar {
/* 40px */
width: 2.5rem;
}
} }
.timeline-modal { .timeline-modal {

View File

@ -14,10 +14,6 @@ $breakpoint-large: screen and (min-width: 60em)
--link-color: #d12991; --link-color: #d12991;
--red: #c61d24; --red: #c61d24;
--dim-color: #333;
--dimmer-color: #999;
--dimmest-color: #bbb;
/* Default theme colors in case the project.css is busted */ /* Default theme colors in case the project.css is busted */
--theme-color: #b1b1b1; --theme-color: #b1b1b1;
--theme-color-dim: #c0c0c0; --theme-color-dim: #c0c0c0;
@ -34,6 +30,8 @@ $breakpoint-large: screen and (min-width: 60em)
--card-background-hover: #f1f1f1; --card-background-hover: #f1f1f1;
--card-background-transparent: #ebebeb00; --card-background-transparent: #ebebeb00;
--timeline-media-background: #b4b4b466;
--bg-1: #f8f8f8; --bg-1: #f8f8f8;
--bg-2: #e8e8e8; --bg-2: #e8e8e8;
--bg-3: #d8d8d8; --bg-3: #d8d8d8;
@ -71,10 +69,6 @@ $breakpoint-large: screen and (min-width: 60em)
--link-color: #ff5dc2; --link-color: #ff5dc2;
--color-error: #ff6666; --color-error: #ff6666;
--dim-color: #bbb;
--dimmer-color: #999;
--dimmest-color: #777;
--theme-color: #666; --theme-color: #666;
--theme-color-dim: #444; --theme-color-dim: #444;
--theme-color-dimmer: #383838; --theme-color-dimmer: #383838;
@ -91,6 +85,8 @@ $breakpoint-large: screen and (min-width: 60em)
--card-background-hover: #333; --card-background-hover: #333;
--card-background-transparent: #24242400; --card-background-transparent: #24242400;
--timeline-media-background: #24242466;
--bg-1: #1f1f1f; --bg-1: #1f1f1f;
--bg-2: #2f2f2f; --bg-2: #2f2f2f;
--bg-3: #494949; --bg-3: #494949;

View File

@ -389,16 +389,17 @@ func TimelineItemsToJSON(items []TimelineItem) string {
builder.WriteString(item.Url) builder.WriteString(item.Url)
builder.WriteString(`",`) builder.WriteString(`",`)
// TODO(redesign): This only serializes a single piece of media.
var mediaType TimelineItemMediaType var mediaType TimelineItemMediaType
var assetUrl string var assetUrl string
var thumbnailUrl string var thumbnailUrl string
var width, height int var width, height int
if len(item.EmbedMedia) > 0 { if len(item.Media) > 0 {
mediaType = item.EmbedMedia[0].Type mediaType = item.Media[0].Type
assetUrl = item.EmbedMedia[0].AssetUrl assetUrl = item.Media[0].AssetUrl
thumbnailUrl = item.EmbedMedia[0].ThumbnailUrl thumbnailUrl = item.Media[0].ThumbnailUrl
width = item.EmbedMedia[0].Width width = item.Media[0].Width
height = item.EmbedMedia[0].Height height = item.Media[0].Height
} }
builder.WriteString(`"media_type":`) builder.WriteString(`"media_type":`)

View File

@ -59,7 +59,7 @@
{{ cleancontrolchars .Description }} {{ cleancontrolchars .Description }}
]]> ]]>
</div> </div>
{{ range .EmbedMedia }} {{ range .Media }}
<div> <div>
{{ if eq .Type mediaimage }} {{ if eq .Type mediaimage }}
<img src="{{ .AssetUrl }}"/> <img src="{{ .AssetUrl }}"/>

View File

@ -1,4 +1,4 @@
<div class="breadcrumbs f7 o-80"> <div class="breadcrumbs f7">
{{ range $i, $breadcrumb := . }} {{ range $i, $breadcrumb := . }}
{{ if gt $i 0 }} » {{ end }} {{ if gt $i 0 }} » {{ end }}
<a href="{{ $breadcrumb.Url }}">{{ $breadcrumb.Name }}</a> <a href="{{ $breadcrumb.Url }}">{{ $breadcrumb.Name }}</a>

View File

@ -16,7 +16,7 @@
<div>{{ trim .Description }}</div> <div>{{ trim .Description }}</div>
</div> </div>
</div> </div>
{{ range .EmbedMedia }} {{ range .Media }}
<div class="flex flex-column {{ if eq .Type mediaembed }}wide-screen{{ end }} justify-stretch iframe-fill"> <div class="flex flex-column {{ if eq .Type mediaembed }}wide-screen{{ end }} justify-stretch iframe-fill">
{{ if eq .Type mediaimage }} {{ if eq .Type mediaimage }}
<img src="{{ .AssetUrl }}"> <img src="{{ .AssetUrl }}">

View File

@ -1,43 +1,64 @@
<div class="timeline-item flex flex-column pa3" data-id="{{ .ID }}" {{ with .FilterTitle }}data-filter-title="{{ . }}"{{ end }}> <div class="timeline-item flex flex-column pa3" data-id="{{ .ID }}" {{ with .FilterTitle }}data-filter-title="{{ . }}"{{ end }}>
{{/* top bar - avatar, info, date */}} {{/* top bar - avatar, info, date */}}
<div class="flex items-center"> <div class="flex items-center">
{{ if .OwnerAvatarUrl }} {{ if .OwnerAvatarUrl }}
<a class="flex flex-shrink-0" href="{{ .OwnerUrl }}"> <a class="flex flex-shrink-0" href="{{ .OwnerUrl }}">
<img class="avatar avatar-user {{ if .SmallInfo }}avatar-small mr2{{ else }}mr3{{ end }}" src="{{ .OwnerAvatarUrl }}" /> <img class="avatar avatar-user {{ if .ForumLayout }}mr3{{ else }}mr2{{ end }}" src="{{ .OwnerAvatarUrl }}" />
</a> </a>
{{ end }} {{ end }}
{{ if .ForumLayout }}
<div class="overflow-hidden flex-grow-1 flex flex-column justify-center"> <div class="overflow-hidden flex-grow-1 flex flex-column g1 justify-center">
{{ if .Breadcrumbs }} {{ with .Breadcrumbs }}
{{ template "breadcrumbs.html" .Breadcrumbs }} {{ template "breadcrumbs.html" . }}
{{ end }} {{ end }}
{{ if .Title }} {{ if .Title }}
<div class="f5 {{ if not .AllowTitleWrap }}nowrap truncate{{ end }}"> <div class="f5 lh-title {{ if not .AllowTitleWrap }}nowrap truncate{{ end }}">
{{ with .TypeTitle }}<b class="dn di-ns">{{ . }}:</b>{{ end }} {{ with .TypeTitle }}<b class="dn di-ns">{{ . }}:</b>{{ end }}
<a href="{{ .Url }}">{{ .Title }}</a> <a href="{{ .Url }}">{{ .Title }}</a>
</div> </div>
{{ end }} {{ end }}
<div class="details"> <div class="details link--normal">
<a class="user" href="{{ .OwnerUrl }}">{{ .OwnerName }}</a> <a class="user" href="{{ .OwnerUrl }}">{{ .OwnerName }}</a>
{{ if not .SmallInfo }} &mdash; {{ timehtml (absoluteshortdate .Date) .Date }}
&mdash; {{ timehtml (relativedate .Date) .Date }}
{{ end }}
</div> </div>
</div> </div>
{{ if .SmallInfo }}
<a href="{{ .Url }}">{{ timehtml (relativedate .Date) .Date }}</a>
{{ end }}
{{ if .Editable }} {{ if .Editable }}
<a href="javascript:;" class="edit ml2">&#9998;</a> <a href="javascript:;" class="edit ml2">&#9998;</a>
<div class="dn rawdesc">{{ .RawDescription }}</div> <div class="dn rawdesc">{{ .RawDescription }}</div>
{{ end }} {{ end }}
{{ else }}
<div class="overflow-hidden flex-grow-1 flex flex-column g1 justify-center link--normal">
{{ if .Breadcrumbs }}
<div>Use .ForumLayout if you want breadcrumbs :)</div>
{{ end }}
{{ if .Title }}
<div>Use .ForumLayout if you want a title :)</div>
{{ end }}
<a class="user b" href="{{ .OwnerUrl }}">{{ .OwnerName }}</a>
<a class="f6" href="{{ .Url }}">{{ timehtml (absoluteshortdate .Date) .Date }}</a>
</div>
{{ if eq (len .Projects) 1 }}
{{ $p := index .Projects 0 }}
<div class="overflow-hidden flex flex-column g1 justify-center link--normal tr">
<a class="user b" href="{{ $p.Url }}">{{ $p.Name }}</a>
</div>
{{ end }}
{{ range .Projects }}
{{ if .Logo }}
<a class="flex flex-shrink-0" href="{{ .Url }}">
<img class="avatar ml2" src="{{ .Logo }}">
</a>
{{ end }}
{{ end }}
{{ end }}
</div> </div>
{{/* content */}} {{/* content */}}
{{ range .EmbedMedia }} {{ range .Media }}
<div class="timeline-content-box mt3 {{ if eq .Type mediaembed }}embed{{ end }} overflow-hidden flex {{ if not (eq .Type mediaunknown) }}timeline-item-bg justify-center{{ end }}"> <div class="timeline-media mt3 {{ if eq .Type mediaembed }}timeline-embed{{ end }} overflow-hidden flex {{ if not (eq .Type mediaunknown) }}justify-center{{ end }}">
{{ if eq .Type mediaimage }} {{ if eq .Type mediaimage }}
<img src="{{ .AssetUrl }}"> <img src="{{ .AssetUrl }}">
{{ else if eq .Type mediavideo }} {{ else if eq .Type mediavideo }}
@ -72,14 +93,7 @@
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ with .Projects }} {{ with .DiscordMessageUrl }}
<div class="mt3 flex g2 projects"> <a class="f7 mt3 i" href="{{ . }}" target="_blank">View original message on Discord</a>
{{ range $i, $proj := . }}
<a data-projid="{{ $proj.ID }}" href="{{ $proj.Url }}" class="snippet-project flex flex-row items-center bg-theme-dimmer ph2 pv1">
<img src="{{ $proj.Logo }}" class="db mr1 br1 h1-5" />
<div>{{ $proj.Name }}</div>
</a>
{{ end }}
</div>
{{ end }} {{ end }}
</div> </div>

View File

@ -2,6 +2,7 @@
{{ define "extrahead" }} {{ define "extrahead" }}
<script src="{{ static "js/templates.js" }}"></script> <script src="{{ static "js/templates.js" }}"></script>
<script src="{{ static "js/tabs.js" }}"></script>
{{ end }} {{ end }}
{{ define "content" }} {{ define "content" }}
@ -87,31 +88,31 @@
<div class="w5 flex flex-column g2 flex-shrink-0"> <div class="w5 flex flex-column g2 flex-shrink-0">
{{ if .User }} {{ if .User }}
<div class="sidebar-card bg--card link--normal"> <div class="sidebar-card bg--card link--normal">
<div onclick="collapse(event)" class="pa2 flex justify-between items-center pointer"> <div onclick="collapse(event)" class="pa3 flex justify-between items-center pointer">
<span class="f7">Your projects</span> <span class="f7">Your projects</span>
<span class="sidebar-chevron svgicon-lite rot-180">{{ svg "chevron-down" }}</span> <span class="sidebar-chevron svgicon-lite rot-180">{{ svg "chevron-down" }}</span>
</div> </div>
<div class="sidebar-card-content"> <div class="sidebar-card-content">
<div class="ph2 flex flex-column g2"> <div class="ph3 flex flex-column g2">
{{ range .UserProjects }} {{ range .UserProjects }}
{{ template "list-project" . }} {{ template "list-project" . }}
{{ else }} {{ else }}
<div class="f7 pv3 tc c--dim">You have not created any projects.</div> <div class="f7 pv3 tc c--dim">You have not created any projects.</div>
{{ end }} {{ end }}
</div> </div>
<a class="bt mt2 pa2 flex justify-between" href="{{ .NewProjectUrl }}"> <a class="bt mt3 pa3 flex justify-between" href="{{ .NewProjectUrl }}">
<div>Create new project</div> <div>Create new project</div>
<div class="svgicon-lite flex items-center">{{ svg "add" }}</div> <div class="svgicon-lite flex items-center">{{ svg "add" }}</div>
</a> </a>
</div> </div>
</div> </div>
<div class="sidebar-card bg--card link--normal"> <div class="sidebar-card bg--card link--normal">
<div onclick="collapse(event)" class="pa2 flex justify-between items-center pointer"> <div onclick="collapse(event)" class="pa3 flex justify-between items-center pointer">
<span class="f7">Following</span> <span class="f7">Following</span>
<span class="sidebar-chevron svgicon-lite rot-180">{{ svg "chevron-down" }}</span> <span class="sidebar-chevron svgicon-lite rot-180">{{ svg "chevron-down" }}</span>
</div> </div>
<div class="sidebar-card-content"> <div class="sidebar-card-content">
<div class="ph2 pb2 flex flex-column g2"> <div class="ph3 pb3 flex flex-column g2">
{{ range .Following }} {{ range .Following }}
{{ template "list-follow" . }} {{ template "list-follow" . }}
{{ else }} {{ else }}
@ -122,11 +123,11 @@
</div> </div>
{{ else }} {{ else }}
<div class="bg--card link--normal"> <div class="bg--card link--normal">
<div class="pa2 flex flex-column g2"> <div class="pa3 flex flex-column g2">
<div class="b">Join the Handmade Network</div> <div class="b">Join the Handmade Network</div>
<div class="f6 post-content">Share your own Handmade projects with the community.</div> <div class="f6 post-content">Share your own Handmade projects with the community.</div>
</div> </div>
<a class="bt pa2 flex justify-between" href="{{ .LoginPageUrl }}"> <a class="bt pa3 flex justify-between" href="{{ .LoginPageUrl }}">
<div>Log in</div> <div>Log in</div>
<div class="svgicon-lite flex items-center">{{ svg "chevron-right" }}</div> <div class="svgicon-lite flex items-center">{{ svg "chevron-right" }}</div>
</a> </a>
@ -139,14 +140,43 @@
<!-- Feed --> <!-- Feed -->
<div class="flex flex-column flex-grow-1 overflow-hidden"> <div class="flex flex-column flex-grow-1 overflow-hidden">
<div class="timeline flex flex-column g3"> <div id="landing-tabs">
<div class="bb mb2 flex f6">
{{ if .User }}
<div data-tab-button="following" class="tab-button ph3 pv1 pointer">Following</div>
{{ end }}
<div data-tab-button="featured" class="tab-button ph3 pv1 pointer">Featured</div>
<div data-tab-button="recent" class="tab-button ph3 pv1 pointer">Recent</div>
<div data-tab-button="news" class="tab-button ph3 pv1 pointer">News</div>
</div>
<div>
{{ if .User }}
<div data-tab="following" class="timeline flex flex-column g3">
{{ range .FollowingItems }}
{{ template "timeline_item.html" . }}
{{ end }}
</div>
{{ end }}
<div data-tab="featured" class="timeline flex flex-column g3">
{{ range .FeaturedItems }}
{{ template "timeline_item.html" . }}
{{ end }}
</div>
<div data-tab="recent" class="timeline flex flex-column g3">
{{ range .RecentItems }} {{ range .RecentItems }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}
</div>
<div data-tab="news" class="timeline flex flex-column g3">
{{ range .NewsItems }}
{{ template "timeline_item.html" . }}
{{ end }}
</div>
TODO: READ MORE LINK TODO: READ MORE LINK
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
<script> <script>
@ -160,6 +190,8 @@
content.hidden = hide; content.hidden = hide;
chevron.classList.toggle("rot-180", !hide); chevron.classList.toggle("rot-180", !hide);
} }
initTabs(document.querySelector("#landing-tabs"));
</script> </script>
{{ end }} {{ end }}

View File

@ -76,6 +76,9 @@
<body class="{{ join " " .BodyClasses }}"> <body class="{{ join " " .BodyClasses }}">
<div class="bg--main m--center mw-site ph3-m ph4-l"> <div class="bg--main m--center mw-site ph3-m ph4-l">
<div class="notice notice-warn mt3 mb2 white ph3 pv2 br2-ns">
We are currently in the process of converting the website to the new design. Some pages, like this one, are still broken. We appreciate your patience.
</div>
{{ template "header.html" . }} {{ template "header.html" . }}
{{ template "notices.html" .Notices }} {{ template "notices.html" .Notices }}
{{ with .Breadcrumbs }} {{ with .Breadcrumbs }}

View File

@ -57,7 +57,7 @@
--theme-color-dimmer: {{ $themeDimmer }}; --theme-color-dimmer: {{ $themeDimmer }};
--theme-color-dimmest: {{ $themeDimmest }}; --theme-color-dimmest: {{ $themeDimmest }};
--timeline-content-background: rgba(255, 255, 255, 0.1); --timeline-media-background: rgba(255, 255, 255, 0.1);
} }
body { body {

View File

@ -57,7 +57,7 @@
--theme-color-dimmer: {{ $themeDimmer }}; --theme-color-dimmer: {{ $themeDimmer }};
--theme-color-dimmest: {{ $themeDimmest }}; --theme-color-dimmest: {{ $themeDimmest }};
--timeline-content-background: rgba(255, 255, 255, 0.1); --timeline-media-background: rgba(255, 255, 255, 0.1);
} }
body { body {

View File

@ -58,7 +58,7 @@
--theme-color-dimmer: {{ $themeDimmer }}; --theme-color-dimmer: {{ $themeDimmer }};
--theme-color-dimmest: {{ $themeDimmest }}; --theme-color-dimmest: {{ $themeDimmest }};
--timeline-content-background: rgba(255, 255, 255, 0.1); --timeline-media-background: rgba(255, 255, 255, 0.1);
} }
body { body {

View File

@ -2,7 +2,6 @@
{{ define "extrahead" }} {{ define "extrahead" }}
{{ template "markdown_previews.html" .TextEditor }} {{ template "markdown_previews.html" .TextEditor }}
<script src="{{ static "js/tabs.js" }}"></script>
<script src="{{ static "js/image_selector.js" }}"></script> <script src="{{ static "js/image_selector.js" }}"></script>
<script src="{{ static "js/templates.js" }}"></script> <script src="{{ static "js/templates.js" }}"></script>
<script src="{{ static "js/base64.js" }}"></script> <script src="{{ static "js/base64.js" }}"></script>

View File

@ -109,6 +109,30 @@ int main() {
<span class="n">GetWindowRect</span><span class="p">(</span> <span class="n">big_window</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">window_rect</span> <span class="p">);</span> <span class="n">GetWindowRect</span><span class="p">(</span> <span class="n">big_window</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">window_rect</span> <span class="p">);</span>
<span class="n">window_rect</span><span class="p">.</span><span class="n">top</span> <span class="o">+=</span> <span class="mi">30</span><span class="p">;</span> <span class="n">window_rect</span><span class="p">.</span><span class="n">top</span> <span class="o">+=</span> <span class="mi">30</span><span class="p">;</span>
</pre> </pre>
<table>
<thead>
<tr>
<th>Items</th>
<th>Expenditure</th>
</tr>
</thead>
<tbody>
<tr>
<th>Donuts</th>
<td>3,000</td>
</tr>
<tr>
<th>Stationery</th>
<td>18,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Totals</th>
<td>21,000</td>
</tr>
</tfoot>
</table>
</div> </div>
<h1 class="mt3 mb2">Form styles</h1> <h1 class="mt3 mb2">Form styles</h1>
@ -212,7 +236,7 @@ int main() {
<h1 class="mt3 mb2">Timeline items</h1> <h1 class="mt3 mb2">Timeline items</h1>
<div class="hmn-form pa3 ba b--theme-dim flex flex-column g3"> <div class="hmn-form pa3 ba b--theme-dim flex flex-column g3 mw7">
{{ range .TestTimelineItems }} {{ range .TestTimelineItems }}
{{ template "timeline_item.html" . }} {{ template "timeline_item.html" . }}
{{ end }} {{ end }}

View File

@ -1,7 +1,6 @@
{{ template "base-2024.html" . }} {{ template "base-2024.html" . }}
{{ define "extrahead" }} {{ define "extrahead" }}
<script src="{{ static "js/tabs.js" }}"></script>
<script src="{{ static "js/image_selector.js" }}"></script> <script src="{{ static "js/image_selector.js" }}"></script>
{{ end }} {{ end }}

View File

@ -338,9 +338,9 @@ type TimelineItem struct {
Description template.HTML Description template.HTML
RawDescription string RawDescription string
EmbedMedia []TimelineItemMedia Media []TimelineItemMedia
SmallInfo bool ForumLayout bool
AllowTitleWrap bool AllowTitleWrap bool
TruncateDescription bool TruncateDescription bool
CanShowcase bool // whether this snippet can be shown in a showcase gallery CanShowcase bool // whether this snippet can be shown in a showcase gallery

View File

@ -188,7 +188,6 @@ func AdminApprovalQueue(c *RequestContext) ResponseData {
} }
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false) timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false)
timelineItem.OwnerAvatarUrl = "" timelineItem.OwnerAvatarUrl = ""
timelineItem.SmallInfo = true
userData.Timeline = append(userData.Timeline, timelineItem) userData.Timeline = append(userData.Timeline, timelineItem)
} }
@ -210,7 +209,6 @@ func AdminApprovalQueue(c *RequestContext) ResponseData {
} }
timelineItem := PostToTimelineItem(hmndata.UrlContextForProject(&p.Project), lineageBuilder, &p.Post, &p.Thread, &p.Author) timelineItem := PostToTimelineItem(hmndata.UrlContextForProject(&p.Project), lineageBuilder, &p.Post, &p.Thread, &p.Author)
timelineItem.OwnerAvatarUrl = "" timelineItem.OwnerAvatarUrl = ""
timelineItem.SmallInfo = true
timelineItem.Description = template.HTML(p.CurrentVersion.TextParsed) timelineItem.Description = template.HTML(p.CurrentVersion.TextParsed)
userData.Timeline = append(userData.Timeline, timelineItem) userData.Timeline = append(userData.Timeline, timelineItem)
} }

View File

@ -16,6 +16,7 @@ func StyleTest(c *RequestContext) ResponseData {
TestTimelineItems: []templates.TimelineItem{ TestTimelineItems: []templates.TimelineItem{
// Forum post // Forum post
{ {
ForumLayout: true,
OwnerName: "Cool User", OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"), OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Now().Add(-5 * time.Second), Date: time.Now().Add(-5 * time.Second),
@ -28,45 +29,73 @@ func StyleTest(c *RequestContext) ResponseData {
Title: "How can I a website?", Title: "How can I a website?",
}, },
// Blog post // Blog post
// Snippet // Snippet with image
{ {
SmallInfo: true,
OwnerName: "Cool User", OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"), OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Date(2022, 3, 20, 13, 32, 54, 0, time.UTC), Date: time.Date(2022, 3, 20, 13, 32, 54, 0, time.UTC),
Url: "test", Url: "test",
DiscordMessageUrl: "test", DiscordMessageUrl: "test",
EmbedMedia: []templates.TimelineItemMedia{ Media: []templates.TimelineItemMedia{
{ {
Type: templates.TimelineItemMediaTypeImage, Type: templates.TimelineItemMediaTypeImage,
AssetUrl: "https://assets.media.handmade.network/32ff3e7e-1d9c-4740-a062-1f8bec2e44cf/unknown.png", AssetUrl: "https://assets.media.handmade.network/32ff3e7e-1d9c-4740-a062-1f8bec2e44cf/unknown.png",
}, },
}, },
Projects: []templates.Project{
{Name: "Cool Project", Logo: "https://assets.media.handmade.network/8c6a3b71-9e91-4bf6-80ef-bc8f3d21b30d/netsim.png"},
},
},
// Snippet with tall image
{
OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Date(2022, 3, 20, 13, 32, 54, 0, time.UTC),
Description: "I got my LaGUI working on Android! 😄",
Url: "test",
DiscordMessageUrl: "https://discord.com/channels/239737791225790464/404399251276169217/1245228715407966208",
Media: []templates.TimelineItemMedia{
{
Type: templates.TimelineItemMediaTypeImage,
AssetUrl: "https://assets.media.handmade.network/ea6f914a-ea00-4cbb-bbd7-586b82fdb484/Screenshot_20240529_120344_com.lagui.simplest.jpg",
},
},
},
// Snippet with video
{
OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Date(2024, 1, 30, 3, 32, 54, 0, time.UTC),
Url: "test",
Description: "Using my newfound decoding knowledge I started working on a simple video editor. I also tried decoding 16 files at once, which didn't seem to bother my 3080 at all.",
DiscordMessageUrl: "https://discord.com/channels/239737791225790464/404399251276169217/1249562779619168266",
Media: []templates.TimelineItemMedia{
{
Type: templates.TimelineItemMediaTypeVideo,
AssetUrl: "https://assets.media.handmade.network/b122c7be-dc6d-41fe-a5ed-033fe991927e/show16.mp4",
ThumbnailUrl: "https://assets.media.handmade.network/b122c7be-dc6d-41fe-a5ed-033fe991927e/b122c7be-dc6d-41fe-a5ed-033fe991927e_thumb.jpg",
},
},
}, },
// Snippet with embed // Snippet with embed
{ {
SmallInfo: true,
OwnerName: "Cool User", OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"), OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Date(2021, 4, 3, 1, 44, 54, 0, time.UTC), Date: time.Date(2021, 4, 3, 1, 44, 54, 0, time.UTC),
Url: "test", Url: "test",
DiscordMessageUrl: "test", DiscordMessageUrl: "test",
EmbedMedia: []templates.TimelineItemMedia{ Media: []templates.TimelineItemMedia{
youtubeMediaItem("FN9hZcTB16g"), youtubeMediaItem("FN9hZcTB16g"),
}, },
Projects: []templates.Project{
{Name: "Cool Project", Logo: templates.UserAvatarDefaultUrl("light")},
},
}, },
// Snippet with two images & multiple projects // Snippet with two images & multiple projects
{ {
SmallInfo: true,
OwnerName: "Cool User", OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"), OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Now().Add(-2 * 24 * time.Hour), Date: time.Now().Add(-2 * 24 * time.Hour),
Url: "test", Url: "test",
DiscordMessageUrl: "test", DiscordMessageUrl: "test",
EmbedMedia: []templates.TimelineItemMedia{ Media: []templates.TimelineItemMedia{
{ {
Type: templates.TimelineItemMediaTypeImage, Type: templates.TimelineItemMediaTypeImage,
AssetUrl: "https://assets.media.handmade.network/979d8850-f6b6-44b4-984e-93be82eb492b/PBR_WIP_20240620_01.png", AssetUrl: "https://assets.media.handmade.network/979d8850-f6b6-44b4-984e-93be82eb492b/PBR_WIP_20240620_01.png",
@ -77,19 +106,18 @@ func StyleTest(c *RequestContext) ResponseData {
}, },
}, },
Projects: []templates.Project{ Projects: []templates.Project{
{Name: "Cool Project", Logo: templates.UserAvatarDefaultUrl("light")}, {Name: "Cool Project", Logo: "https://assets.media.handmade.network/8c6a3b71-9e91-4bf6-80ef-bc8f3d21b30d/netsim.png"},
{Name: "Uncool Project"}, {Name: "Uncool Project"},
}, },
}, },
// Snippet with a video and an image // Snippet with a video and an image
{ {
SmallInfo: true,
OwnerName: "Cool User", OwnerName: "Cool User",
OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"), OwnerAvatarUrl: templates.UserAvatarDefaultUrl("dark"),
Date: time.Now().Add(-2 * time.Hour), Date: time.Now().Add(-2 * time.Hour),
Url: "test", Url: "test",
DiscordMessageUrl: "test", DiscordMessageUrl: "test",
EmbedMedia: []templates.TimelineItemMedia{ Media: []templates.TimelineItemMedia{
{ {
Type: templates.TimelineItemMediaTypeImage, Type: templates.TimelineItemMediaTypeImage,
AssetUrl: "https://assets.media.handmade.network/979d8850-f6b6-44b4-984e-93be82eb492b/PBR_WIP_20240620_01.png", AssetUrl: "https://assets.media.handmade.network/979d8850-f6b6-44b4-984e-93be82eb492b/PBR_WIP_20240620_01.png",
@ -100,8 +128,7 @@ func StyleTest(c *RequestContext) ResponseData {
}, },
}, },
Projects: []templates.Project{ Projects: []templates.Project{
{Name: "Cool Project", Logo: templates.UserAvatarDefaultUrl("light")}, {Name: "Project without logo"},
{Name: "Uncool Project"},
}, },
}, },
// Snippet with every type of embed at once // Snippet with every type of embed at once

View File

@ -271,7 +271,6 @@ func getLJ2024FeedData(c *RequestContext, maxTimelineItems int) (JamFeedDataLJ20
timelineItems = make([]templates.TimelineItem, 0, len(snippets)) timelineItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets { for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false) timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false)
timelineItem.SmallInfo = true
timelineItems = append(timelineItems, timelineItem) timelineItems = append(timelineItems, timelineItem)
} }
} }
@ -451,7 +450,6 @@ func JamFeed2023(c *RequestContext) ResponseData {
timelineItems = make([]templates.TimelineItem, 0, len(snippets)) timelineItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets { for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false) timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false)
timelineItem.SmallInfo = true
timelineItems = append(timelineItems, timelineItem) timelineItems = append(timelineItems, timelineItem)
} }
} }
@ -619,7 +617,6 @@ func JamFeed2023_Visibility(c *RequestContext) ResponseData {
timelineItems = make([]templates.TimelineItem, 0, len(snippets)) timelineItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets { for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false) timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false)
timelineItem.SmallInfo = true
timelineItems = append(timelineItems, timelineItem) timelineItems = append(timelineItems, timelineItem)
} }
} }
@ -832,7 +829,6 @@ func JamFeed2022(c *RequestContext) ResponseData {
timelineItems = make([]templates.TimelineItem, 0, len(snippets)) timelineItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets { for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false) timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, false)
timelineItem.SmallInfo = true
timelineItems = append(timelineItems, timelineItem) timelineItems = append(timelineItems, timelineItem)
} }
} }

View File

@ -20,7 +20,6 @@ func Index(c *RequestContext) ResponseData {
type LandingTemplateData struct { type LandingTemplateData struct {
templates.BaseData templates.BaseData
NewsPost *templates.TimelineItem
FollowingItems []templates.TimelineItem FollowingItems []templates.TimelineItem
FeaturedItems []templates.TimelineItem FeaturedItems []templates.TimelineItem
RecentItems []templates.TimelineItem RecentItems []templates.TimelineItem
@ -56,6 +55,24 @@ func Index(c *RequestContext) ResponseData {
} }
} }
featuredProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
FeaturedOnly: true,
})
if err != nil {
c.Logger.Warn().Err(err).Msg("failed to fetch featured projects")
}
var featuredProjectIDs []int
for _, p := range featuredProjects {
featuredProjectIDs = append(featuredProjectIDs, p.Project.ID)
}
featuredItems, err = FetchTimeline(c, c.Conn, c.CurrentUser, TimelineQuery{
ProjectIDs: featuredProjectIDs,
Limit: 100,
})
if err != nil {
c.Logger.Warn().Err(err).Msg("failed to fetch featured feed")
}
recentItems, err = FetchTimeline(c, c.Conn, c.CurrentUser, TimelineQuery{ recentItems, err = FetchTimeline(c, c.Conn, c.CurrentUser, TimelineQuery{
Limit: 100, Limit: 100,
}) })
@ -63,27 +80,24 @@ func Index(c *RequestContext) ResponseData {
c.Logger.Warn().Err(err).Msg("failed to fetch recent feed") c.Logger.Warn().Err(err).Msg("failed to fetch recent feed")
} }
c.Perf.StartBlock("SQL", "Get news")
newsThreads, err := hmndata.FetchThreads(c, c.Conn, c.CurrentUser, hmndata.ThreadsQuery{ newsThreads, err := hmndata.FetchThreads(c, c.Conn, c.CurrentUser, hmndata.ThreadsQuery{
ProjectIDs: []int{models.HMNProjectID}, ProjectIDs: []int{models.HMNProjectID},
ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost}, ThreadTypes: []models.ThreadType{models.ThreadTypeProjectBlogPost},
Limit: 1, Limit: 100,
OrderByCreated: true,
}) })
if err != nil { if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post")) return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news threads"))
} }
var newsPostItem *templates.TimelineItem for _, t := range newsThreads {
if len(newsThreads) > 0 { item := PostToTimelineItem(c.UrlContext, lineageBuilder, &t.FirstPost, &t.Thread, t.FirstPostAuthor)
t := newsThreads[0]
item := PostToTimelineItem(hmndata.UrlContextForProject(&t.Project), lineageBuilder, &t.FirstPost, &t.Thread, t.FirstPostAuthor)
item.Breadcrumbs = nil item.Breadcrumbs = nil
item.TypeTitle = "" item.TypeTitle = ""
item.AllowTitleWrap = true
item.Description = template.HTML(t.FirstPostCurrentVersion.TextParsed) item.Description = template.HTML(t.FirstPostCurrentVersion.TextParsed)
item.AllowTitleWrap = true
item.TruncateDescription = true item.TruncateDescription = true
newsPostItem = &item newsItems = append(newsItems, item)
} }
c.Perf.EndBlock()
var projects []templates.Project var projects []templates.Project
if c.CurrentUser != nil { if c.CurrentUser != nil {
@ -117,7 +131,6 @@ func Index(c *RequestContext) ResponseData {
err = res.WriteTemplate("landing.html", LandingTemplateData{ err = res.WriteTemplate("landing.html", LandingTemplateData{
BaseData: baseData, BaseData: baseData,
NewsPost: newsPostItem,
FollowingItems: followingItems, FollowingItems: followingItems,
FeaturedItems: featuredItems, FeaturedItems: featuredItems,
RecentItems: recentItems, RecentItems: recentItems,

View File

@ -58,7 +58,6 @@ func Snippet(c *RequestContext) ResponseData {
canEdit := (c.CurrentUser != nil && (c.CurrentUser.IsStaff || c.CurrentUser.ID == s.Owner.ID)) canEdit := (c.CurrentUser != nil && (c.CurrentUser.IsStaff || c.CurrentUser.ID == s.Owner.ID))
snippet := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, canEdit) snippet := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, canEdit)
snippet.SmallInfo = true
opengraph := []templates.OpenGraphItem{ opengraph := []templates.OpenGraphItem{
{Property: "og:site_name", Value: "Handmade Network"}, {Property: "og:site_name", Value: "Handmade Network"},
@ -68,8 +67,8 @@ func Snippet(c *RequestContext) ResponseData {
{Property: "og:description", Value: string(snippet.Description)}, {Property: "og:description", Value: string(snippet.Description)},
} }
if len(snippet.EmbedMedia) > 0 { if len(snippet.Media) > 0 {
media := snippet.EmbedMedia[0] media := snippet.Media[0]
switch media.Type { switch media.Type {
case templates.TimelineItemMediaTypeImage: case templates.TimelineItemMediaTypeImage:

View File

@ -148,7 +148,6 @@ func FetchTimeline(ctx context.Context, conn db.ConnOrTx, currentUser *models.Us
s.Owner, s.Owner,
false, false,
) )
item.SmallInfo = true
timelineItems = append(timelineItems, item) timelineItems = append(timelineItems, item)
} }
@ -300,6 +299,8 @@ func PostToTimelineItem(
OwnerAvatarUrl: ownerTmpl.AvatarUrl, OwnerAvatarUrl: ownerTmpl.AvatarUrl,
OwnerName: ownerTmpl.Name, OwnerName: ownerTmpl.Name,
OwnerUrl: ownerTmpl.ProfileUrl, OwnerUrl: ownerTmpl.ProfileUrl,
ForumLayout: true,
} }
if typeTitles, ok := TimelineTypeTitleMap[post.ThreadType]; ok { if typeTitles, ok := TimelineTypeTitleMap[post.ThreadType]; ok {
@ -349,7 +350,7 @@ func TwitchStreamToTimelineItem(
OwnerName: ownerName, OwnerName: ownerName,
OwnerUrl: ownerUrl, OwnerUrl: ownerUrl,
SmallInfo: true, ForumLayout: true,
} }
return item return item
@ -382,26 +383,26 @@ func SnippetToTimelineItem(
if asset != nil { if asset != nil {
if strings.HasPrefix(asset.MimeType, "image/") { if strings.HasPrefix(asset.MimeType, "image/") {
item.EmbedMedia = append(item.EmbedMedia, imageMediaItem(asset)) item.Media = append(item.Media, imageMediaItem(asset))
} else if strings.HasPrefix(asset.MimeType, "video/") { } else if strings.HasPrefix(asset.MimeType, "video/") {
item.EmbedMedia = append(item.EmbedMedia, videoMediaItem(asset)) item.Media = append(item.Media, videoMediaItem(asset))
} else if strings.HasPrefix(asset.MimeType, "audio/") { } else if strings.HasPrefix(asset.MimeType, "audio/") {
item.EmbedMedia = append(item.EmbedMedia, audioMediaItem(asset)) item.Media = append(item.Media, audioMediaItem(asset))
} else { } else {
item.EmbedMedia = append(item.EmbedMedia, unknownMediaItem(asset)) item.Media = append(item.Media, unknownMediaItem(asset))
} }
} }
if snippet.Url != nil { if snippet.Url != nil {
url := *snippet.Url url := *snippet.Url
if videoId := getYoutubeVideoID(url); videoId != "" { if videoId := getYoutubeVideoID(url); videoId != "" {
item.EmbedMedia = append(item.EmbedMedia, youtubeMediaItem(videoId)) item.Media = append(item.Media, youtubeMediaItem(videoId))
item.CanShowcase = false item.CanShowcase = false
} }
} }
if len(item.EmbedMedia) == 0 || if len(item.Media) == 0 ||
(len(item.EmbedMedia) > 0 && (item.EmbedMedia[0].Width == 0 || item.EmbedMedia[0].Height == 0)) { (len(item.Media) > 0 && (item.Media[0].Width == 0 || item.Media[0].Height == 0)) {
item.CanShowcase = false item.CanShowcase = false
} }

View File

@ -14,6 +14,7 @@
- [x] column - [x] column
- [ ] content - [ ] content
- [ ] description - [ ] description
- [ ] c--dim and friends
- [ ] Re-evaluate form styles - [ ] Re-evaluate form styles
- [ ] theme-color-light is used only for buttons - [ ] theme-color-light is used only for buttons
- [x] center-layout vs. margin-center - [x] center-layout vs. margin-center