commit d33195c234281017be8241f8afebb49a400d70b0
parent 894f416af1ede25f0ae161d3ba4c90c0fb4c1c67
Author: brookjeynes <me@brookjeynes.dev>
Date: Fri, 19 Jun 2026 08:49:19 +1000
ui: style add artist page
Task-id: yqlqrzkxypsltypk
Signed-off-by: brookjeynes <me@brookjeynes.dev>
Diffstat:
10 files changed, 107 insertions(+), 55 deletions(-)
diff --git a/internal/server/new-artist.go b/internal/server/new-artist.go
@@ -12,10 +12,11 @@ import (
)
type NewArtistForm struct {
- Name string `schema:"artist"`
- Bio string `schema:"biography"`
- LatinName string `schema:"latin-name"`
- Disambiguation string `schema:"disambiguation"`
+ Name string `schema:"artist"`
+ Bio string `schema:"biography"`
+ LatinName string `schema:"latin-name"`
+ Disambiguation string `schema:"disambiguation"`
+ MusicBrainzArtistID domain.MusicBrainzID `schema:"musicbrainz-artist-id"`
}
func parseNewArtistForm(form NewArtistForm) (domain.Artist, error) {
@@ -56,11 +57,21 @@ func parseNewArtistForm(form NewArtistForm) (domain.Artist, error) {
disambiguation = &form.Disambiguation
}
+ // musicbrainz release id
+ var musicbrainzArtistID *domain.MusicBrainzID
+ if form.MusicBrainzArtistID != "" {
+ if !domain.MusicBrainzIDIsValid(string(form.MusicBrainzArtistID)) {
+ return artist, fmt.Errorf("'%s' must be a valid MusicBrainz ID", form.MusicBrainzArtistID)
+ }
+ musicbrainzArtistID = &form.MusicBrainzArtistID
+ }
+
artist = domain.Artist{
- Name: form.Name,
- LatinName: latinName,
- Bio: bio,
- Disambiguation: disambiguation,
+ Name: form.Name,
+ LatinName: latinName,
+ Bio: bio,
+ Disambiguation: disambiguation,
+ MusicBrainzArtistID: musicbrainzArtistID,
}
return artist, nil
diff --git a/internal/server/ui/partials/similar-artists-list/similar-artists-list.templ b/internal/server/ui/partials/similar-artists-list/similar-artists-list.templ
@@ -1,24 +1,28 @@
package similarartistslist
import "fmt"
+import "lyrics/internal/server/routes"
templ SimilarArtistsList(params SimilarArtistsListParams) {
- <ul id="similar-artists">
- for _, artist := range params.Artists {
- {{
- value := artist.Name
+ if len(params.Artists) >= 1 {
+ <p class="section-label -muted">Similar artists</p>
+ <ul>
+ for _, artist := range params.Artists {
+ {{
+ value := artist.Name
- if artist.LatinName != nil && artist.Disambiguation != nil {
- value = fmt.Sprintf("%s (%s, %s)", artist.Name, *artist.LatinName, *artist.Disambiguation)
- } else if artist.LatinName != nil {
- value = fmt.Sprintf("%s (%s)", artist.Name, *artist.LatinName)
- } else if artist.Disambiguation != nil {
- value = fmt.Sprintf("%s (%s)", artist.Name, *artist.Disambiguation)
- }
- }}
- <li title={ value }>
- { value }
- </li>
- }
- </ul>
+ if artist.LatinName != nil && artist.Disambiguation != nil {
+ value = fmt.Sprintf("%s (%s, %s)", artist.Name, *artist.LatinName, *artist.Disambiguation)
+ } else if artist.LatinName != nil {
+ value = fmt.Sprintf("%s (%s)", artist.Name, *artist.LatinName)
+ } else if artist.Disambiguation != nil {
+ value = fmt.Sprintf("%s (%s)", artist.Name, *artist.Disambiguation)
+ }
+ }}
+ <li title={ value }>
+ <a href={ routes.Artist(artist) }>{ value }</a>
+ </li>
+ }
+ </ul>
+ }
}
diff --git a/internal/server/ui/views/edit-release/edit-release.templ b/internal/server/ui/views/edit-release/edit-release.templ
@@ -210,7 +210,7 @@ templ EditRelease(params EditReleaseParams) {
</fieldset>
<fieldset>
<legend>MusicBrainz</legend>
- <label title="External identifier used for metadata lookups such as cover art. This app does not contact MusicBrainz when saving.">
+ <label title="External identifier used for metadata lookups such as cover art.">
Release ID (optional):
<input
name="musicbrainz-release-id"
diff --git a/internal/server/ui/views/new-artist/new-artist.css b/internal/server/ui/views/new-artist/new-artist.css
@@ -0,0 +1,16 @@
+.artist-form {
+ max-width: 42rem;
+
+ fieldset {
+ margin: 1.5rem 0;
+ }
+
+ textarea {
+ min-height: 18rem;
+ margin-top: 0.5rem;
+ border: 1px solid var(--dark-gray);
+ width: 100%;
+ padding: 0.75rem;
+ resize: vertical;
+ }
+}
diff --git a/internal/server/ui/views/new-artist/new-artist.templ b/internal/server/ui/views/new-artist/new-artist.templ
@@ -6,21 +6,23 @@ import (
)
templ NewArtist(params NewArtistParams) {
- @base.Base(base.BaseParams{Title: "new artist"}) {
+ @base.Base(base.BaseParams{Title: "new artist", PageStylesheets: []string{"new-artist.css"}}) {
<h1>Add an artist</h1>
<form
+ class="artist-form"
hx-post="/artist/new"
hx-swap="none"
hx-disable-elt="#add-artist-btn"
>
<fieldset x-data="{ showLatinName: false }">
+ <legend>Name</legend>
<label title="prefer the artists name in their original language. use the latin name input for english names">
artist:
<input
hx-get="/artist/search?format=similar"
hx-trigger="input changed delay:500ms, load"
hx-target="#similar-artists"
- hx-swap="outerHTML"
+ hx-swap="innerHTML"
name="artist"
type="text"
pattern={ domain.NoTrailingOrLeadingSpaceRegex.String() }
@@ -38,31 +40,42 @@ templ NewArtist(params NewArtistParams) {
/>
</label>
</fieldset>
- <label title="helps users distinguish between artists with identical or similar names, e.g. South Korean singer-songwriter">
- disambiguation:
- <input
- name="disambiguation"
- type="text"
- pattern={ domain.NoTrailingOrLeadingSpaceRegex.String() }
- title="helps users distinguish between artists with identical or similar names, e.g. South Korean singer-songwriter"
- />
- <i data-lucide="info"></i>
- </label>
- <div>
- <span>similar artists</span>
- <ul id="similar-artists"></ul>
- </div>
- <label>
- biography:
- <textarea
- name="biography"
- rows="20"
- spellcheck
- ></textarea>
- </label>
- <button id="add-artist-btn" type="submit">
- add artist
- </button>
+ <section id="similar-artists" class="similar-artists" aria-label="Similar artists"></section>
+ <fieldset>
+ <legend>Disambiguation</legend>
+ <label title="helps users distinguish between artists with identical or similar names, e.g. South Korean singer-songwriter">
+ disambiguation:
+ <input
+ name="disambiguation"
+ type="text"
+ pattern={ domain.NoTrailingOrLeadingSpaceRegex.String() }
+ />
+ <i data-lucide="info"></i>
+ </label>
+ </fieldset>
+ <fieldset>
+ <legend>Biography</legend>
+ <label>
+ biography:
+ <textarea name="biography" rows="16" spellcheck></textarea>
+ </label>
+ </fieldset>
+ <fieldset>
+ <legend>MusicBrainz</legend>
+ <label title="External identifier used for metadata lookups such as cover art.">
+ Artist ID (optional):
+ <input
+ name="musicbrainz-artist-id"
+ type="text"
+ pattern={ domain.IsValidMusicBrainzIDPattern }
+ placeholder="e32a4ae3-67d1-4541-84d8-de6ab2d7dec1"
+ />
+ <i data-lucide="info"></i>
+ </label>
+ </fieldset>
+ <footer aria-label="Form actions">
+ <button class="-primary" id="add-artist-btn" type="submit">add artist</button>
+ </footer>
</form>
}
}
diff --git a/internal/server/ui/views/new-release/new-release.templ b/internal/server/ui/views/new-release/new-release.templ
@@ -140,7 +140,7 @@ templ NewRelease(params NewReleaseParams) {
</fieldset>
<fieldset>
<legend>MusicBrainz</legend>
- <label title="External identifier used for metadata lookups such as cover art. This app does not contact MusicBrainz when saving.">
+ <label title="External identifier used for metadata lookups such as cover art.">
Release ID (optional):
<input
name="musicbrainz-release-id"
diff --git a/internal/server/ui/views/new-song/new-song.templ b/internal/server/ui/views/new-song/new-song.templ
@@ -294,7 +294,7 @@ templ NewSong(params NewSongParams) {
</fieldset>
<fieldset>
<legend>MusicBrainz</legend>
- <label title="External identifier used for metadata lookups such as cover art. This app does not contact MusicBrainz when saving.">
+ <label title="External identifier used for metadata lookups such as cover art.">
Recording ID (optional):
<input
name="musicbrainz-recording-id"
diff --git a/scripts/build-assets.sh b/scripts/build-assets.sh
@@ -16,6 +16,7 @@ minify internal/server/ui/style.css -o static/files/style.css
minify internal/server/toast/toast.css -o static/files/toast.css
minify internal/server/ui/views/song/song.css -o static/files/song.css
minify internal/server/ui/views/artist/artist.css -o static/files/artist.css
+minify internal/server/ui/views/new-artist/new-artist.css -o static/files/new-artist.css
minify internal/server/ui/views/release/release.css -o static/files/release.css
minify internal/server/ui/views/new-release/new-release.css -o static/files/new-release.css
minify internal/server/ui/views/new-song/new-song.css -o static/files/new-song.css
diff --git a/tasks/style_and_improve_ux_for_new_artist_form.txt b/tasks/done/style_and_improve_ux_for_new_artist_form.txt
diff --git a/tasks/pull_artist_metadata_from_musicbrainz.txt b/tasks/pull_artist_metadata_from_musicbrainz.txt
@@ -0,0 +1,7 @@
+pull artist metadata from musicbrainz @feat +artist
+id: wznxmyrtuxlplrxu
+
+we can pull a lot of data from musicbrainz that users might want to see
+
+ $ curl musicbrainz.org/ws/2/artist/<artist-id> \
+ -H "Accept: application/json" -H "User-Agent: lyrics/0.1"