diff --git a/public/parsing.wasm b/public/parsing.wasm index 556c6716..be7f22b3 100755 Binary files a/public/parsing.wasm and b/public/parsing.wasm differ diff --git a/public/style.css b/public/style.css index 3ad6707d..d64546ad 100644 --- a/public/style.css +++ b/public/style.css @@ -8484,6 +8484,23 @@ span.icon-rss::before { height: var(--image-size); object-fit: cover; } +.project-header-img { + width: 100%; + height: var(--height-5); + background-color: rgba(255, 255, 255, 0.5); + background-size: cover; +} +.project-links { + background-color: var(--c-transparent-background); + display: flex; + align-items: center; + --link-color: var(--color); + font-weight: bold; +} +.project-links::after { + content: "\200b"; + padding: var(--spacing-2) 0; +} /* src/rawdata/scss/showcase.css */ .showcase-item .gradient { diff --git a/src/links/links.go b/src/links/links.go new file mode 100644 index 00000000..9d29d0d9 --- /dev/null +++ b/src/links/links.go @@ -0,0 +1,94 @@ +package links + +import "regexp" + +// +// This is all in its own package so we can compile it to wasm without building extra junk. +// + +// An online site/service for which we recognize the link +type Service struct { + Name string + IconName string + Regex *regexp.Regexp +} + +var Services = []Service{ + // { + // Name: "itch.io", + // IconName: "itch", + // Regex: regexp.MustCompile(`://(?P[\w-]+)\.itch\.io`), + // }, + { + Name: "App Store", + IconName: "app-store", + Regex: regexp.MustCompile(`^https?://apps.apple.com`), + }, + { + Name: "Bluesky", + IconName: "bluesky", + Regex: regexp.MustCompile(`^https?://bsky.app/profile/(?P[\w.-]+)$`), + }, + { + Name: "Discord", + IconName: "discord", + Regex: regexp.MustCompile(`^https?://discord\.gg`), + }, + { + Name: "GitHub", + IconName: "github", + Regex: regexp.MustCompile(`^https?://github\.com/(?P[\w/-]+)`), + }, + { + Name: "GitLab", + IconName: "gitlab", + Regex: regexp.MustCompile(`^https?://gitlab\.com/(?P[\w/-]+)`), + }, + { + Name: "Google Play", + IconName: "google-play", + Regex: regexp.MustCompile(`^https?://play\.google\.com`), + }, + { + Name: "Patreon", + IconName: "patreon", + Regex: regexp.MustCompile(`^https?://patreon\.com/(?P[\w-]+)`), + }, + { + Name: "Twitch", + IconName: "twitch", + Regex: regexp.MustCompile(`^https?://twitch\.tv/(?P[\w/-]+)`), + }, + { + Name: "Twitter", + IconName: "twitter", + Regex: regexp.MustCompile(`^https?://(twitter|x)\.com/(?P\w+)`), + }, + { + Name: "Vimeo", + IconName: "vimeo", + Regex: regexp.MustCompile(`^https?://vimeo\.com/(?P\w+)`), + }, + { + Name: "YouTube", + IconName: "youtube", + Regex: regexp.MustCompile(`youtube\.com/(c/)?(?P[@\w/-]+)$`), + }, +} + +func ParseKnownServicesForUrl(url string) (service Service, username string) { + for _, svc := range Services { + match := svc.Regex.FindStringSubmatch(url) + if match != nil { + username := "" + if idx := svc.Regex.SubexpIndex("username"); idx >= 0 { + username = match[idx] + } + + return svc, username + } + } + return Service{ + IconName: "website", + }, "" +} diff --git a/src/parsing/wasm/parsingmain.go b/src/parsing/wasm/parsingmain.go index 9790b4c4..3893f134 100644 --- a/src/parsing/wasm/parsingmain.go +++ b/src/parsing/wasm/parsingmain.go @@ -5,16 +5,25 @@ package main import ( "syscall/js" + "git.handmade.network/hmn/hmn/src/links" "git.handmade.network/hmn/hmn/src/parsing" ) func main() { - js.Global().Set("parseMarkdown", js.FuncOf(func(this js.Value, args []js.Value) interface{} { + js.Global().Set("parseMarkdown", js.FuncOf(func(this js.Value, args []js.Value) any { return parsing.ParseMarkdown(args[0].String(), parsing.ForumPreviewMarkdown) })) - js.Global().Set("parseMarkdownEdu", js.FuncOf(func(this js.Value, args []js.Value) interface{} { + js.Global().Set("parseMarkdownEdu", js.FuncOf(func(this js.Value, args []js.Value) any { return parsing.ParseMarkdown(args[0].String(), parsing.EducationPreviewMarkdown) })) + js.Global().Set("parseKnownServicesForUrl", js.FuncOf(func(this js.Value, args []js.Value) any { + service, username := links.ParseKnownServicesForUrl(args[0].String()) + return js.ValueOf(map[string]any{ + "service": service.Name, + "icon": service.IconName, + "username": username, + }) + })) var done chan struct{} <-done // block forever diff --git a/src/rawdata/scss/projects.css b/src/rawdata/scss/projects.css index 2cd143fc..97fa5831 100644 --- a/src/rawdata/scss/projects.css +++ b/src/rawdata/scss/projects.css @@ -71,4 +71,27 @@ width: var(--image-size); height: var(--image-size); object-fit: cover; +} + +.project-header-img { + /* w-100 h5 bg-white-50 bg-center cover */ + width: 100%; + height: var(--height-5); + /* TODO(redesign): Better placeholder */ + background-color: rgba(255, 255, 255, 0.5); + background-size: cover; +} + +.project-links { + background-color: var(--c-transparent-background); + display: flex; + align-items: center; + --link-color: var(--color); + font-weight: bold; + + /* make sure secondary links render at the right height despite SVG size */ + &::after { + content: '\200b'; + padding: var(--spacing-2) 0; + } } \ No newline at end of file diff --git a/src/templates/mapping.go b/src/templates/mapping.go index c513a52b..0794d544 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -4,13 +4,13 @@ import ( "fmt" "html/template" "net/netip" - "regexp" "strconv" "strings" "git.handmade.network/hmn/hmn/src/calendar" "git.handmade.network/hmn/hmn/src/hmndata" "git.handmade.network/hmn/hmn/src/hmnurl" + "git.handmade.network/hmn/hmn/src/links" "git.handmade.network/hmn/hmn/src/models" ) @@ -240,95 +240,8 @@ var UnknownUser = User{ AvatarUrl: UserAvatarUrl(nil), } -// An online site/service for which we recognize the link -type LinkService struct { - Name string - IconName string - Regex *regexp.Regexp -} - -var LinkServices = []LinkService{ - // { - // Name: "itch.io", - // IconName: "itch", - // Regex: regexp.MustCompile(`://(?P[\w-]+)\.itch\.io`), - // }, - { - Name: "App Store", - IconName: "app-store", - Regex: regexp.MustCompile(`^https?://apps.apple.com`), - }, - { - Name: "Bluesky", - IconName: "bluesky", - Regex: regexp.MustCompile(`^https?://bsky.app/profile/(?P[\w.-]+)$`), - }, - { - Name: "Discord", - IconName: "discord", - Regex: regexp.MustCompile(`^https?://discord\.gg`), - }, - { - Name: "GitHub", - IconName: "github", - Regex: regexp.MustCompile(`^https?://github\.com/(?P[\w/-]+)`), - }, - { - Name: "GitLab", - IconName: "gitlab", - Regex: regexp.MustCompile(`^https?://gitlab\.com/(?P[\w/-]+)`), - }, - { - Name: "Google Play", - IconName: "google-play", - Regex: regexp.MustCompile(`^https?://play\.google\.com`), - }, - { - Name: "Patreon", - IconName: "patreon", - Regex: regexp.MustCompile(`^https?://patreon\.com/(?P[\w-]+)`), - }, - { - Name: "Twitch", - IconName: "twitch", - Regex: regexp.MustCompile(`^https?://twitch\.tv/(?P[\w/-]+)`), - }, - { - Name: "Twitter", - IconName: "twitter", - Regex: regexp.MustCompile(`^https?://(twitter|x)\.com/(?P\w+)`), - }, - { - Name: "Vimeo", - IconName: "vimeo", - Regex: regexp.MustCompile(`^https?://vimeo\.com/(?P\w+)`), - }, - { - Name: "YouTube", - IconName: "youtube", - Regex: regexp.MustCompile(`youtube\.com/(c/)?(?P[@\w/-]+)$`), - }, -} - -func ParseKnownServicesForLink(link *models.Link) (service LinkService, username string) { - for _, svc := range LinkServices { - match := svc.Regex.FindStringSubmatch(link.URL) - if match != nil { - username := "" - if idx := svc.Regex.SubexpIndex("username"); idx >= 0 { - username = match[idx] - } - - return svc, username - } - } - return LinkService{ - IconName: "website", - }, "" -} - func LinkToTemplate(link *models.Link) Link { - service, username := ParseKnownServicesForLink(link) + service, username := links.ParseKnownServicesForUrl(link.URL) return Link{ Name: link.Name, Url: link.URL, diff --git a/src/templates/src/include/link_editor.html b/src/templates/src/include/link_editor.html index 98bf78a8..0b5863dc 100644 --- a/src/templates/src/include/link_editor.html +++ b/src/templates/src/include/link_editor.html @@ -5,14 +5,14 @@
@@ -51,6 +51,8 @@ function addLink(e) { e.preventDefault(); linksContainer.appendChild(linkTemplate().root); + + fireLinkEditEvent(); } function deleteLink(e) { @@ -59,6 +61,8 @@ l.remove(); ensureLinksEmptyState(); + + fireLinkEditEvent(); } function ensureLinksEmptyState() { @@ -169,9 +173,19 @@ document.body.classList.remove("grabbing"); draggingLink = null; + + fireLinkEditEvent(); } window.addEventListener("mouseup", endLinkDrag); window.addEventListener("mousemove", doLinkDrag); + + function linkInput(e) { + fireLinkEditEvent(); + } + + function fireLinkEditEvent() { + window.dispatchEvent(new Event("linkedit")); + } diff --git a/src/templates/src/project_edit.html b/src/templates/src/project_edit.html index 9ec86e09..24531e9d 100644 --- a/src/templates/src/project_edit.html +++ b/src/templates/src/project_edit.html @@ -6,9 +6,20 @@ + + +