diff --git a/api/go.mod b/api/go.mod index 0158f17..a25dc9f 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,7 +4,7 @@ go 1.22.2 require ( github.com/hashicorp/go-cleanhttp v0.5.2 - github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240913181521-3708ddadc4d2 + github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240920110319-6925911f7f64 golang.org/x/net v0.25.0 ) diff --git a/api/go.sum b/api/go.sum index 632e8d2..6bd492b 100644 --- a/api/go.sum +++ b/api/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240913181521-3708ddadc4d2 h1:HasYzw37auhdq6nHXyvgEocAxeTPxRUjczIdLfkTdmY= -github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240913181521-3708ddadc4d2/go.mod h1:xnbFN3Wrw2B1F/D7pvjjwoeXSt+njRI31ccPFno0BM0= +github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240920110319-6925911f7f64 h1:yeoqi6Ur2IdboPLZU4I5XR+VzSQVEH+EwW4kPhun/EU= +github.com/jonashiltl/openchangelog/apitypes v0.0.0-20240920110319-6925911f7f64/go.mod h1:xnbFN3Wrw2B1F/D7pvjjwoeXSt+njRI31ccPFno0BM0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/apitypes/changelog.go b/apitypes/changelog.go index 41f62df..216a3a9 100644 --- a/apitypes/changelog.go +++ b/apitypes/changelog.go @@ -8,16 +8,17 @@ import ( // Represents the Changelog returned by the API via json encoding. // Implements json un-/marshaling. type Changelog struct { - ID string - WorkspaceID string - Subdomain string - Domain NullString - Title NullString - Subtitle NullString - ColorScheme ColorScheme - Logo Logo - Source Source - CreatedAt time.Time + ID string + WorkspaceID string + Subdomain string + Domain NullString + Title NullString + Subtitle NullString + ColorScheme ColorScheme + HidePoweredBy bool + Logo Logo + Source Source + CreatedAt time.Time } type ColorScheme string @@ -30,25 +31,27 @@ const ( func (l Changelog) MarshalJSON() ([]byte, error) { obj := struct { - ID string `json:"id"` - WorkspaceID string `json:"workspaceId"` - Subdomain string `json:"subdomain,omitempty"` - Title string `json:"title,omitempty"` - Domain string `json:"domain,omitempty"` - Subtitle string `json:"subtitle,omitempty"` - ColorScheme string `json:"colorScheme,omitempty"` - Logo *Logo `json:"logo,omitempty"` - Source Source `json:"source,omitempty"` - CreatedAt *time.Time `json:"createdAt,omitempty"` + ID string `json:"id"` + WorkspaceID string `json:"workspaceId"` + Subdomain string `json:"subdomain,omitempty"` + Title string `json:"title,omitempty"` + Domain string `json:"domain,omitempty"` + Subtitle string `json:"subtitle,omitempty"` + ColorScheme string `json:"colorScheme,omitempty"` + HidePoweredBy bool `json:"hidePoweredBy"` + Logo *Logo `json:"logo,omitempty"` + Source Source `json:"source,omitempty"` + CreatedAt *time.Time `json:"createdAt,omitempty"` }{ - ID: l.ID, - WorkspaceID: l.WorkspaceID, - Subdomain: l.Subdomain, - Domain: l.Domain.V(), - Title: l.Title.V(), - Subtitle: l.Subtitle.V(), - ColorScheme: string(l.ColorScheme), - Source: l.Source, + ID: l.ID, + WorkspaceID: l.WorkspaceID, + Subdomain: l.Subdomain, + Domain: l.Domain.V(), + Title: l.Title.V(), + Subtitle: l.Subtitle.V(), + ColorScheme: string(l.ColorScheme), + HidePoweredBy: l.HidePoweredBy, + Source: l.Source, } if l.Logo.IsValid() { @@ -119,6 +122,13 @@ func (c *Changelog) UnmarshalJSON(b []byte) error { } } + if hidePoweredByRaw, ok := objMap["hidePoweredBy"]; ok { + err = json.Unmarshal(*hidePoweredByRaw, &c.HidePoweredBy) + if err != nil { + return err + } + } + if logoRaw, ok := objMap["logo"]; ok { err = json.Unmarshal(*logoRaw, &c.Logo) if err != nil { @@ -207,14 +217,20 @@ func (l Logo) IsValid() bool { } type CreateChangelogBody struct { - Title NullString `json:"title"` - Subtitle NullString `json:"subtitle"` - Logo Logo `json:"logo"` - Domain NullString `json:"domain"` - ColorScheme ColorScheme `json:"colorScheme"` + Title NullString `json:"title"` + Subtitle NullString `json:"subtitle"` + Logo Logo `json:"logo"` + Domain NullString `json:"domain"` + ColorScheme ColorScheme `json:"colorScheme"` + HidePoweredBy bool `json:"hidePoweredBy"` } type UpdateChangelogBody struct { - CreateChangelogBody - Subdomain NullString `json:"subdomain"` + Title NullString `json:"title"` + Subtitle NullString `json:"subtitle"` + Logo Logo `json:"logo"` + Domain NullString `json:"domain"` + ColorScheme ColorScheme `json:"colorScheme"` + Subdomain NullString `json:"subdomain"` + HidePoweredBy *bool `json:"hidePoweredBy,omitempty"` } diff --git a/apitypes/changelog_test.go b/apitypes/changelog_test.go index d54047f..e365311 100644 --- a/apitypes/changelog_test.go +++ b/apitypes/changelog_test.go @@ -38,8 +38,9 @@ func TestChangelogMarshaling(t *testing.T) { Repo: "openchangelog", Path: ".testdata", }, - ColorScheme: Dark, - CreatedAt: now, + ColorScheme: Dark, + HidePoweredBy: true, + CreatedAt: now, }, expect: fmt.Sprintf(`{ "id": "cl_xxxx", @@ -63,23 +64,26 @@ func TestChangelogMarshaling(t *testing.T) { "repo": "openchangelog", "path": ".testdata" }, + "hidePoweredBy": true, "colorScheme": "dark", "createdAt": "%s" }`, nowStr), }, { input: Changelog{ - ID: "cl_xxxx", - WorkspaceID: "ws_xxxx", - Title: NewString("Test Title"), - ColorScheme: System, - CreatedAt: now, + ID: "cl_xxxx", + WorkspaceID: "ws_xxxx", + Title: NewString("Test Title"), + ColorScheme: System, + HidePoweredBy: false, + CreatedAt: now, }, expect: fmt.Sprintf(`{ "id": "cl_xxxx", "workspaceId": "ws_xxxx", "title": "Test Title", "colorScheme": "system", + "hidePoweredBy": false, "createdAt": "%s" }`, nowStr), }, @@ -94,6 +98,7 @@ func TestChangelogMarshaling(t *testing.T) { expect: `{ "id": "cl_xxxx", "workspaceId": "ws_xxxx", + "hidePoweredBy": false, "logo": { "alt": "test" } @@ -127,8 +132,9 @@ func TestChangelogUnmarshal(t *testing.T) { Repo: "openchangelog", Path: ".testdata", }, - ColorScheme: Dark, - CreatedAt: time.Unix(1715958564, 0).UTC(), + HidePoweredBy: true, + ColorScheme: Dark, + CreatedAt: time.Unix(1715958564, 0).UTC(), }, { ID: "cl_xxxx", @@ -158,6 +164,7 @@ func TestChangelogUnmarshal(t *testing.T) { } func TestUpdateChangelogBodyMarshal(t *testing.T) { + hidePoweredBy := true tests := []struct { name string input UpdateChangelogBody @@ -178,9 +185,7 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { { name: "valid title", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Title: NewString("test"), - }, + Title: NewString("test"), }, expected: `{ "title": "test", @@ -194,9 +199,7 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { { name: "null title", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Title: NewNullString(), - }, + Title: NewNullString(), }, expected: `{ "title": null, @@ -210,10 +213,8 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { { name: "valid logo", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Logo: Logo{ - Src: NewString("test"), - }, + Logo: Logo{ + Src: NewString("test"), }, }, expected: `{ @@ -230,9 +231,7 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { { name: "valid color scheme", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - ColorScheme: Dark, - }, + ColorScheme: Dark, }, expected: `{ "title": "", @@ -243,6 +242,21 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { "colorScheme": "dark" }`, }, + { + name: "valid hide powered by", + input: UpdateChangelogBody{ + HidePoweredBy: &hidePoweredBy, + }, + expected: `{ + "title": "", + "subtitle": "", + "logo": {}, + "domain": "", + "subdomain": "", + "colorScheme": "", + "hidePoweredBy": true + }`, + }, } for _, test := range tests { @@ -258,6 +272,7 @@ func TestUpdateChangelogBodyMarshal(t *testing.T) { } func TestUpdateChangelogBodyUnmarshal(t *testing.T) { + hidePoweredBy := true tests := []struct { name string input UpdateChangelogBody @@ -269,45 +284,41 @@ func TestUpdateChangelogBodyUnmarshal(t *testing.T) { { name: "valid title", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Title: NewString("test"), - }, + Title: NewString("test"), }, }, { name: "null title", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Title: NewNullString(), - }, + Title: NewNullString(), }, }, { name: "valid logo", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Logo: Logo{ - Src: NewString("test"), - }, + Logo: Logo{ + Src: NewString("test"), }, }, }, { name: "null logo src", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - Logo: Logo{ - Src: NewNullString(), - }, + Logo: Logo{ + Src: NewNullString(), }, }, }, { name: "valid color scheme", input: UpdateChangelogBody{ - CreateChangelogBody: CreateChangelogBody{ - ColorScheme: Dark, - }, + ColorScheme: Dark, + }, + }, + { + name: "valid hide powered by", + input: UpdateChangelogBody{ + HidePoweredBy: &hidePoweredBy, }, }, } diff --git a/apitypes/null.go b/apitypes/null.go index f33a9c6..512c3e5 100644 --- a/apitypes/null.go +++ b/apitypes/null.go @@ -106,3 +106,13 @@ func NewNullString() NullString { } type NullColorScheme = Null[ColorScheme] + +type NullBool = Null[bool] + +func NewBool(b bool) NullBool { + return NewValue(b) +} + +func NewNullBool() NullBool { + return NewNull[bool]() +} diff --git a/components/footer.templ b/components/footer.templ index 957f0ba..2460484 100644 --- a/components/footer.templ +++ b/components/footer.templ @@ -1,9 +1,15 @@ package components -templ Footer() { - -} \ No newline at end of file +type FooterArgs struct { + HidePoweredBy bool +} + +templ Footer(args FooterArgs) { + +} diff --git a/components/footer_templ.go b/components/footer_templ.go index 7418b30..ce7844d 100644 --- a/components/footer_templ.go +++ b/components/footer_templ.go @@ -8,7 +8,11 @@ package components import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" -func Footer() templ.Component { +type FooterArgs struct { + HidePoweredBy bool +} + +func Footer(args FooterArgs) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -26,7 +30,17 @@ func Footer() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/components/theme.templ b/components/theme.templ index fc08252..369a272 100644 --- a/components/theme.templ +++ b/components/theme.templ @@ -31,7 +31,7 @@ templ Theme(args ThemeArgs) { }
li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"\201C""\201D""\2018""\2019";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:initial;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;text-align:start;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:initial}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:17 24 39;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.relative{position:relative}.sticky{position:sticky}.top-0{top:0}.z-\[1\]{z-index:1}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-8{margin-top:2rem;margin-bottom:2rem}.mb-4{margin-bottom:1rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-8{margin-top:2rem}.flex{display:flex}.h-10{height:2.5rem}.h-24{height:6rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.max-h-full{max-height:100%}.w-3\/4{width:75%}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-full{max-width:100%}.max-w-prose{max-width:65ch}.flex-1{flex:1 1 0%}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.rounded{border-radius:.25rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-solid{border-style:solid}.border-b-black\/10{border-bottom-color:#0000001a}.border-t-gray-200{--tw-border-opacity:1;border-top-color:rgb(229 231 235/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pt-10{padding-top:2.5rem}.text-center{text-align:center}.text-caption{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.opacity-0{opacity:0}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media (min-width:640px){.sm\:prose-base{font-size:1rem;line-height:1.75}.sm\:prose-base :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.sm\:prose-base :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.sm\:prose-base :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.sm\:prose-base :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.sm\:prose-base :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.sm\:prose-base :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.sm\:prose-base :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.sm\:prose-base :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.sm\:prose-base :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.sm\:prose-base :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.sm\:prose-base :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.sm\:prose-base :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.sm\:prose-base :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.sm\:prose-base :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.sm\:prose-base :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.sm\:prose-base :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.sm\:prose-base :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.sm\:prose-base :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.sm\:prose-base :where(.sm\:prose-base>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.sm\:prose-base :where(.sm\:prose-base>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(.sm\:prose-base>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.sm\:prose-base :where(.sm\:prose-base>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(.sm\:prose-base>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.sm\:prose-base :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.sm\:prose-base :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.sm\:prose-base :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.sm\:prose-base :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3em;margin-bottom:3em}.sm\:prose-base :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857}.sm\:prose-base :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.sm\:prose-base :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.sm\:prose-base :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.sm\:prose-base :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.sm\:prose-base :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.sm\:prose-base :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.sm\:prose-base :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.sm\:prose-base :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.sm\:prose-base :where(.sm\:prose-base>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(.sm\:prose-base>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}}.dark\:prose-invert:where([color-scheme=dark],[color-scheme=dark] *){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.hover\:bg-orange-400\/10:hover{background-color:#fb923c1a}.hover\:text-orange-400:hover{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}.group:hover .group-hover\:opacity-100{opacity:1}.prose-h1\:mb-3 :is(:where(h1):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.75rem}.prose-h2\:mb-2 :is(:where(h2):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.5rem}.prose-h3\:mb-2 :is(:where(h3):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.5rem}.prose-p\:my-2 :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){margin-top:.5rem;margin-bottom:.5rem}.prose-p\:leading-6 :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){line-height:1.5rem}.prose-a\:text-primary :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.prose-a\:no-underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-decoration-line:none}.prose-a\:underline-offset-4 :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-underline-offset:4px}.hover\:prose-a\:text-primary-highlight :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))):hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:prose-a\:underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))):hover{text-decoration-line:underline}.prose-img\:rounded :is(:where(img):not(:where([class~=not-prose],[class~=not-prose] *))){border-radius:.25rem}@media (min-width:640px){.sm\:mx-0{margin-left:0;margin-right:0}.sm\:mt-10{margin-top:2.5rem}.sm\:px-0{padding-left:0;padding-right:0}}@media (min-width:768px){.md\:mt-20{margin-top:5rem}}@media (min-width:1024px){.lg\:absolute{position:absolute}.lg\:-left-40{left:-10rem}.lg\:top-0{top:0}.lg\:\!mt-1{margin-top:.25rem!important}}.dark\:border-b-white\/10:where([color-scheme=dark],[color-scheme=dark] *){border-bottom-color:#ffffff1a}.dark\:border-t-gray-800:where([color-scheme=dark],[color-scheme=dark] *){--tw-border-opacity:1;border-top-color:rgb(31 41 55/var(--tw-border-opacity))}.dark\:bg-neutral-950:where([color-scheme=dark],[color-scheme=dark] *){--tw-bg-opacity:1;background-color:rgb(10 10 10/var(--tw-bg-opacity))}.dark\:bg-white\/5:where([color-scheme=dark],[color-scheme=dark] *){background-color:#ffffff0d}.dark\:text-neutral-400:where([color-scheme=dark],[color-scheme=dark] *){--tw-text-opacity:1;color:rgb(163 163 163/var(--tw-text-opacity))}.dark\:text-white:where([color-scheme=dark],[color-scheme=dark] *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.\[\&\:not\(\.htmx-request\)\]\:h-0:not(.htmx-request){height:0}.\[\&\:not\(\.htmx-request\)\]\:overflow-hidden:not(.htmx-request){overflow:hidden}.\[\&\:not\(\.htmx-request\)\]\:opacity-0:not(.htmx-request){opacity:0} \ No newline at end of file +/*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);text-decoration:underline;font-weight:500}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{font-weight:400;color:var(--tw-prose-counters)}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-top:3em;margin-bottom:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-style:italic;color:var(--tw-prose-quotes);border-inline-start-width:.25rem;border-inline-start-color:var(--tw-prose-quote-borders);quotes:"\201C""\201D""\2018""\2019";margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:800;font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:900;color:inherit}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:700;font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:800;color:inherit}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:700;color:inherit}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-top:2em;margin-bottom:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-weight:500;font-family:inherit;color:var(--tw-prose-kbd);box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-weight:600;font-size:.875em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-pre-code);background-color:var(--tw-prose-pre-bg);overflow-x:auto;font-weight:400;font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:initial;border-width:0;border-radius:0;padding:0;font-weight:inherit;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){width:100%;table-layout:auto;text-align:start;margin-top:2em;margin-bottom:2em;font-size:.875em;line-height:1.7142857}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-th-borders)}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;vertical-align:bottom;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:1px;border-bottom-color:var(--tw-prose-td-borders)}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:initial}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-width:1px;border-top-color:var(--tw-prose-th-borders)}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:17 24 39;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.prose-sm{font-size:.875rem;line-height:1.7142857}.prose-sm :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;line-height:1.5555556;margin-top:.8888889em;margin-bottom:.8888889em}.prose-sm :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.3333333em;margin-bottom:1.3333333em;padding-inline-start:1.1111111em}.prose-sm :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.1428571em;margin-top:0;margin-bottom:.8em;line-height:1.2}.prose-sm :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.4285714em;margin-top:1.6em;margin-bottom:.8em;line-height:1.4}.prose-sm :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.2857143em;margin-top:1.5555556em;margin-bottom:.4444444em;line-height:1.5555556}.prose-sm :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.4285714em;margin-bottom:.5714286em;line-height:1.4285714}.prose-sm :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;border-radius:.3125rem;padding-top:.1428571em;padding-inline-end:.3571429em;padding-bottom:.1428571em;padding-inline-start:.3571429em}.prose-sm :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em}.prose-sm :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.prose-sm :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8888889em}.prose-sm :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.6666667;margin-top:1.6666667em;margin-bottom:1.6666667em;border-radius:.25rem;padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em;padding-inline-start:1.5714286em}.prose-sm :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;margin-bottom:.2857143em}.prose-sm :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.4285714em}.prose-sm :where(.prose-sm>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(.prose-sm>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(.prose-sm>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.1428571em}.prose-sm :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5714286em;margin-bottom:.5714286em}.prose-sm :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em;margin-bottom:1.1428571em}.prose-sm :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.1428571em}.prose-sm :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.2857143em;padding-inline-start:1.5714286em}.prose-sm :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2.8571429em;margin-bottom:2.8571429em}.prose-sm :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.5}.prose-sm :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.6666667em;padding-inline-end:1em;padding-bottom:.6666667em;padding-inline-start:1em}.prose-sm :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose-sm :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose-sm :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.7142857em;margin-bottom:1.7142857em}.prose-sm :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.prose-sm :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.8571429em;line-height:1.3333333;margin-top:.6666667em}.prose-sm :where(.prose-sm>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose-sm :where(.prose-sm>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.relative{position:relative}.sticky{position:sticky}.top-0{top:0}.z-\[1\]{z-index:1}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-8{margin-top:2rem;margin-bottom:2rem}.mb-4{margin-bottom:1rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-8{margin-top:2rem}.flex{display:flex}.h-10{height:2.5rem}.h-24{height:6rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.max-h-full{max-height:100%}.min-h-full{min-height:100%}.w-3\/4{width:75%}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-full{max-width:100%}.max-w-prose{max-width:65ch}.flex-1{flex:1 1 0%}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.rounded{border-radius:.25rem}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-solid{border-style:solid}.border-b-black\/10{border-bottom-color:#0000001a}.border-t-gray-200{--tw-border-opacity:1;border-top-color:rgb(229 231 235/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-neutral-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity))}.object-contain{-o-object-fit:contain;object-fit:contain}.p-1{padding:.25rem}.px-4{padding-left:1rem;padding-right:1rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pt-10{padding-top:2.5rem}.text-center{text-align:center}.text-caption{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.opacity-0{opacity:0}.backdrop-blur-md{--tw-backdrop-blur:blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}@media (min-width:640px){.sm\:prose-base{font-size:1rem;line-height:1.75}.sm\:prose-base :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.sm\:prose-base :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;line-height:1.6;margin-top:1.2em;margin-bottom:1.2em}.sm\:prose-base :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.6em;margin-bottom:1.6em;padding-inline-start:1em}.sm\:prose-base :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:2.25em;margin-top:0;margin-bottom:.8888889em;line-height:1.1111111}.sm\:prose-base :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.5em;margin-top:2em;margin-bottom:1em;line-height:1.3333333}.sm\:prose-base :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:1.25em;margin-top:1.6em;margin-bottom:.6em;line-height:1.6}.sm\:prose-base :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.5em;margin-bottom:.5em;line-height:1.5}.sm\:prose-base :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.sm\:prose-base :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;border-radius:.3125rem;padding-top:.1875em;padding-inline-end:.375em;padding-bottom:.1875em;padding-inline-start:.375em}.sm\:prose-base :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.sm\:prose-base :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em}.sm\:prose-base :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.9em}.sm\:prose-base :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857;margin-top:1.7142857em;margin-bottom:1.7142857em;border-radius:.375rem;padding-top:.8571429em;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-inline-start:1.1428571em}.sm\:prose-base :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.sm\:prose-base :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em;padding-inline-start:1.625em}.sm\:prose-base :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;margin-bottom:.5em}.sm\:prose-base :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.sm\:prose-base :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.sm\:prose-base :where(.sm\:prose-base>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.sm\:prose-base :where(.sm\:prose-base>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(.sm\:prose-base>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.sm\:prose-base :where(.sm\:prose-base>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(.sm\:prose-base>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.sm\:prose-base :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.75em;margin-bottom:.75em}.sm\:prose-base :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em;margin-bottom:1.25em}.sm\:prose-base :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.sm\:prose-base :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.sm\:prose-base :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:3em;margin-bottom:3em}.sm\:prose-base :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857}.sm\:prose-base :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.sm\:prose-base :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.sm\:prose-base :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.sm\:prose-base :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-top:.5714286em;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em}.sm\:prose-base :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.sm\:prose-base :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.sm\:prose-base :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:2em;margin-bottom:2em}.sm\:prose-base :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0;margin-bottom:0}.sm\:prose-base :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.sm\:prose-base :where(.sm\:prose-base>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.sm\:prose-base :where(.sm\:prose-base>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}}.dark\:prose-invert:where([color-scheme=dark],[color-scheme=dark] *){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.hover\:bg-orange-400\/10:hover{background-color:#fb923c1a}.hover\:text-orange-400:hover{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}.group:hover .group-hover\:opacity-100{opacity:1}.prose-h1\:mb-3 :is(:where(h1):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.75rem}.prose-h2\:mb-2 :is(:where(h2):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.5rem}.prose-h3\:mb-2 :is(:where(h3):not(:where([class~=not-prose],[class~=not-prose] *))){margin-bottom:.5rem}.prose-p\:my-2 :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){margin-top:.5rem;margin-bottom:.5rem}.prose-p\:leading-6 :is(:where(p):not(:where([class~=not-prose],[class~=not-prose] *))){line-height:1.5rem}.prose-a\:text-primary :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.prose-a\:no-underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-decoration-line:none}.prose-a\:underline-offset-4 :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))){text-underline-offset:4px}.hover\:prose-a\:text-primary-highlight :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))):hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:prose-a\:underline :is(:where(a):not(:where([class~=not-prose],[class~=not-prose] *))):hover{text-decoration-line:underline}.prose-img\:rounded :is(:where(img):not(:where([class~=not-prose],[class~=not-prose] *))){border-radius:.25rem}@media (min-width:640px){.sm\:mx-0{margin-left:0;margin-right:0}.sm\:mt-10{margin-top:2.5rem}.sm\:px-0{padding-left:0;padding-right:0}}@media (min-width:768px){.md\:mt-20{margin-top:5rem}}@media (min-width:1024px){.lg\:absolute{position:absolute}.lg\:-left-40{left:-10rem}.lg\:top-0{top:0}.lg\:\!mt-1{margin-top:.25rem!important}}.dark\:border-b-white\/10:where([color-scheme=dark],[color-scheme=dark] *){border-bottom-color:#ffffff1a}.dark\:border-t-gray-800:where([color-scheme=dark],[color-scheme=dark] *){--tw-border-opacity:1;border-top-color:rgb(31 41 55/var(--tw-border-opacity))}.dark\:bg-neutral-950:where([color-scheme=dark],[color-scheme=dark] *){--tw-bg-opacity:1;background-color:rgb(10 10 10/var(--tw-bg-opacity))}.dark\:bg-white\/5:where([color-scheme=dark],[color-scheme=dark] *){background-color:#ffffff0d}.dark\:text-neutral-400:where([color-scheme=dark],[color-scheme=dark] *){--tw-text-opacity:1;color:rgb(163 163 163/var(--tw-text-opacity))}.dark\:text-white:where([color-scheme=dark],[color-scheme=dark] *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.\[\&\:not\(\.htmx-request\)\]\:h-0:not(.htmx-request){height:0}.\[\&\:not\(\.htmx-request\)\]\:overflow-hidden:not(.htmx-request){overflow:hidden}.\[\&\:not\(\.htmx-request\)\]\:opacity-0:not(.htmx-request){opacity:0} \ No newline at end of file diff --git a/internal/handler/web/views/index.templ b/internal/handler/web/views/index.templ index fe6190d..5fe2d9d 100644 --- a/internal/handler/web/views/index.templ +++ b/internal/handler/web/views/index.templ @@ -12,6 +12,7 @@ type IndexArgs struct { components.Logo components.HeaderArgs components.ArticleListArgs + components.FooterArgs } templ Index(arg IndexArgs) { @@ -28,7 +29,7 @@ templ Index(arg IndexArgs) { } @components.ArticleList(arg.ArticleListArgs) } - @components.Footer() + @components.Footer(arg.FooterArgs) } } } diff --git a/internal/handler/web/views/index_templ.go b/internal/handler/web/views/index_templ.go index ecd4cda..3689cec 100644 --- a/internal/handler/web/views/index_templ.go +++ b/internal/handler/web/views/index_templ.go @@ -20,6 +20,7 @@ type IndexArgs struct { components.Logo components.HeaderArgs components.ArticleListArgs + components.FooterArgs } func Index(arg IndexArgs) templ.Component { @@ -162,7 +163,7 @@ func Index(arg IndexArgs) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = components.Footer().Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = components.Footer(arg.FooterArgs).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/internal/store/config.go b/internal/store/config.go index a9d4808..e8629b6 100644 --- a/internal/store/config.go +++ b/internal/store/config.go @@ -44,6 +44,7 @@ func (s *configStore) GetChangelog(ctx context.Context, wID WorkspaceID, cID Cha if s.cfg.Page != nil { cl.Title = apitypes.NewString(s.cfg.Page.Title) cl.Subtitle = apitypes.NewString(s.cfg.Page.Subtitle) + cl.HidePoweredBy = s.cfg.Page.HidePoweredBy switch strings.ToLower(s.cfg.Page.ColorScheme) { case System.String(): cl.ColorScheme = System diff --git a/internal/store/models.go b/internal/store/models.go index 776dcad..cb33480 100644 --- a/internal/store/models.go +++ b/internal/store/models.go @@ -11,20 +11,21 @@ import ( ) type changelog struct { - ID string - WorkspaceID string - Subdomain string - Title apitypes.NullString - Subtitle apitypes.NullString - SourceID apitypes.NullString - LogoSrc apitypes.NullString - LogoLink apitypes.NullString - LogoAlt apitypes.NullString - LogoHeight apitypes.NullString - LogoWidth apitypes.NullString - CreatedAt int64 - Domain apitypes.NullString - ColorScheme ColorScheme + ID string + WorkspaceID string + Subdomain string + Title apitypes.NullString + Subtitle apitypes.NullString + SourceID apitypes.NullString + LogoSrc apitypes.NullString + LogoLink apitypes.NullString + LogoAlt apitypes.NullString + LogoHeight apitypes.NullString + LogoWidth apitypes.NullString + CreatedAt int64 + Domain apitypes.NullString + ColorScheme ColorScheme + HidePoweredBy int64 } type changelogSource struct { diff --git a/internal/store/query.sql b/internal/store/query.sql index 1be76a6..d00c5dc 100644 --- a/internal/store/query.sql +++ b/internal/store/query.sql @@ -36,8 +36,8 @@ LIMIT 1; -- name: createChangelog :one INSERT INTO changelogs ( - workspace_id, id, subdomain, domain, title, subtitle, logo_src, logo_link, logo_alt, logo_height, logo_width, color_scheme -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + workspace_id, id, subdomain, domain, title, subtitle, logo_src, logo_link, logo_alt, logo_height, logo_width, color_scheme, hide_powered_by +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *; -- name: deleteChangelog :exec @@ -68,6 +68,7 @@ WHERE c.workspace_id = ?; UPDATE changelogs SET subdomain = coalesce(sqlc.narg(subdomain), subdomain), + hide_powered_by = coalesce(sqlc.narg(hide_powered_by), hide_powered_by), title = CASE WHEN cast(@set_title as bool) THEN @title ELSE title END, subtitle = CASE WHEN cast(@set_subtitle as bool) THEN @subtitle ELSE subtitle END, domain = CASE WHEN cast(@set_domain as bool) THEN @domain ELSE domain END, diff --git a/internal/store/query.sql.go b/internal/store/query.sql.go index 3d51515..21bca26 100644 --- a/internal/store/query.sql.go +++ b/internal/store/query.sql.go @@ -7,30 +7,32 @@ package store import ( "context" + "database/sql" "github.com/jonashiltl/openchangelog/apitypes" ) const createChangelog = `-- name: createChangelog :one INSERT INTO changelogs ( - workspace_id, id, subdomain, domain, title, subtitle, logo_src, logo_link, logo_alt, logo_height, logo_width, color_scheme -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) -RETURNING id, workspace_id, subdomain, title, subtitle, source_id, logo_src, logo_link, logo_alt, logo_height, logo_width, created_at, domain, color_scheme + workspace_id, id, subdomain, domain, title, subtitle, logo_src, logo_link, logo_alt, logo_height, logo_width, color_scheme, hide_powered_by +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +RETURNING id, workspace_id, subdomain, title, subtitle, source_id, logo_src, logo_link, logo_alt, logo_height, logo_width, created_at, domain, color_scheme, hide_powered_by ` type createChangelogParams struct { - WorkspaceID string - ID string - Subdomain string - Domain apitypes.NullString - Title apitypes.NullString - Subtitle apitypes.NullString - LogoSrc apitypes.NullString - LogoLink apitypes.NullString - LogoAlt apitypes.NullString - LogoHeight apitypes.NullString - LogoWidth apitypes.NullString - ColorScheme ColorScheme + WorkspaceID string + ID string + Subdomain string + Domain apitypes.NullString + Title apitypes.NullString + Subtitle apitypes.NullString + LogoSrc apitypes.NullString + LogoLink apitypes.NullString + LogoAlt apitypes.NullString + LogoHeight apitypes.NullString + LogoWidth apitypes.NullString + ColorScheme ColorScheme + HidePoweredBy int64 } func (q *Queries) createChangelog(ctx context.Context, arg createChangelogParams) (changelog, error) { @@ -47,6 +49,7 @@ func (q *Queries) createChangelog(ctx context.Context, arg createChangelogParams arg.LogoHeight, arg.LogoWidth, arg.ColorScheme, + arg.HidePoweredBy, ) var i changelog err := row.Scan( @@ -64,6 +67,7 @@ func (q *Queries) createChangelog(ctx context.Context, arg createChangelogParams &i.CreatedAt, &i.Domain, &i.ColorScheme, + &i.HidePoweredBy, ) return i, err } @@ -180,7 +184,7 @@ func (q *Queries) deleteWorkspace(ctx context.Context, id string) error { } const getChangelog = `-- name: getChangelog :one -SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id +SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, c.hide_powered_by, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id FROM changelogs c LEFT JOIN changelog_source cs ON c.workspace_id = cs.workspace_id AND c.source_id = cs.id WHERE c.workspace_id = ? AND c.id = ? @@ -214,6 +218,7 @@ func (q *Queries) getChangelog(ctx context.Context, arg getChangelogParams) (get &i.changelog.CreatedAt, &i.changelog.Domain, &i.changelog.ColorScheme, + &i.changelog.HidePoweredBy, &i.ChangelogSource.ID, &i.ChangelogSource.WorkspaceID, &i.ChangelogSource.Owner, @@ -225,7 +230,7 @@ func (q *Queries) getChangelog(ctx context.Context, arg getChangelogParams) (get } const getChangelogByDomainOrSubdomain = `-- name: getChangelogByDomainOrSubdomain :one -SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id +SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, c.hide_powered_by, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id FROM changelogs c LEFT JOIN changelog_source cs ON c.workspace_id = cs.workspace_id AND c.source_id = cs.id WHERE c.domain = ? OR c.subdomain = ? @@ -261,6 +266,7 @@ func (q *Queries) getChangelogByDomainOrSubdomain(ctx context.Context, arg getCh &i.changelog.CreatedAt, &i.changelog.Domain, &i.changelog.ColorScheme, + &i.changelog.HidePoweredBy, &i.ChangelogSource.ID, &i.ChangelogSource.WorkspaceID, &i.ChangelogSource.Owner, @@ -347,7 +353,7 @@ func (q *Queries) getWorkspace(ctx context.Context, id string) (getWorkspaceRow, } const listChangelogs = `-- name: listChangelogs :many -SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id +SELECT c.id, c.workspace_id, c.subdomain, c.title, c.subtitle, c.source_id, c.logo_src, c.logo_link, c.logo_alt, c.logo_height, c.logo_width, c.created_at, c.domain, c.color_scheme, c.hide_powered_by, cs.id, cs.workspace_id, cs.owner, cs.repo, cs.path, cs.installation_id FROM changelogs c LEFT JOIN changelog_source cs ON c.workspace_id = cs.workspace_id AND c.source_id = cs.id WHERE c.workspace_id = ? @@ -382,6 +388,7 @@ func (q *Queries) listChangelogs(ctx context.Context, workspaceID string) ([]lis &i.changelog.CreatedAt, &i.changelog.Domain, &i.changelog.ColorScheme, + &i.changelog.HidePoweredBy, &i.ChangelogSource.ID, &i.ChangelogSource.WorkspaceID, &i.ChangelogSource.Owner, @@ -479,21 +486,23 @@ const updateChangelog = `-- name: updateChangelog :one UPDATE changelogs SET subdomain = coalesce(?1, subdomain), - title = CASE WHEN cast(?2 as bool) THEN ?3 ELSE title END, - subtitle = CASE WHEN cast(?4 as bool) THEN ?5 ELSE subtitle END, - domain = CASE WHEN cast(?6 as bool) THEN ?7 ELSE domain END, - logo_src = CASE WHEN cast(?8 as bool) THEN ?9 ELSE logo_src END, - logo_link = CASE WHEN cast(?10 as bool) THEN ?11 ELSE logo_link END, - logo_alt = CASE WHEN cast(?12 as bool) THEN ?13 ELSE logo_alt END, - logo_height = CASE WHEN cast(?14 as bool) THEN ?15 ELSE logo_height END, - logo_width = CASE WHEN cast(?16 as bool) THEN ?17 ELSE logo_width END, - color_scheme = CASE WHEN cast(?18 as bool) THEN ?19 ELSE color_scheme END -WHERE workspace_id = ?20 AND id = ?21 -RETURNING id, workspace_id, subdomain, title, subtitle, source_id, logo_src, logo_link, logo_alt, logo_height, logo_width, created_at, domain, color_scheme + hide_powered_by = coalesce(?2, hide_powered_by), + title = CASE WHEN cast(?3 as bool) THEN ?4 ELSE title END, + subtitle = CASE WHEN cast(?5 as bool) THEN ?6 ELSE subtitle END, + domain = CASE WHEN cast(?7 as bool) THEN ?8 ELSE domain END, + logo_src = CASE WHEN cast(?9 as bool) THEN ?10 ELSE logo_src END, + logo_link = CASE WHEN cast(?11 as bool) THEN ?12 ELSE logo_link END, + logo_alt = CASE WHEN cast(?13 as bool) THEN ?14 ELSE logo_alt END, + logo_height = CASE WHEN cast(?15 as bool) THEN ?16 ELSE logo_height END, + logo_width = CASE WHEN cast(?17 as bool) THEN ?18 ELSE logo_width END, + color_scheme = CASE WHEN cast(?19 as bool) THEN ?20 ELSE color_scheme END +WHERE workspace_id = ?21 AND id = ?22 +RETURNING id, workspace_id, subdomain, title, subtitle, source_id, logo_src, logo_link, logo_alt, logo_height, logo_width, created_at, domain, color_scheme, hide_powered_by ` type updateChangelogParams struct { Subdomain apitypes.NullString + HidePoweredBy sql.NullInt64 SetTitle bool Title apitypes.NullString SetSubtitle bool @@ -519,6 +528,7 @@ type updateChangelogParams struct { func (q *Queries) updateChangelog(ctx context.Context, arg updateChangelogParams) (changelog, error) { row := q.db.QueryRowContext(ctx, updateChangelog, arg.Subdomain, + arg.HidePoweredBy, arg.SetTitle, arg.Title, arg.SetSubtitle, @@ -556,6 +566,7 @@ func (q *Queries) updateChangelog(ctx context.Context, arg updateChangelogParams &i.CreatedAt, &i.Domain, &i.ColorScheme, + &i.HidePoweredBy, ) return i, err } diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go index ad64836..f2d237c 100644 --- a/internal/store/sqlite.go +++ b/internal/store/sqlite.go @@ -16,20 +16,21 @@ import ( func (cl changelog) toExported(source changelogSource) Changelog { c := Changelog{ - WorkspaceID: WorkspaceID(cl.WorkspaceID), - ID: ChangelogID(cl.ID), - Subdomain: Subdomain(cl.Subdomain), - Domain: Domain(cl.Domain), - Title: cl.Title, - Subtitle: cl.Subtitle, - LogoSrc: cl.LogoSrc, - LogoLink: cl.LogoLink, - LogoAlt: cl.LogoAlt, - LogoHeight: cl.LogoHeight, - LogoWidth: cl.LogoWidth, - ColorScheme: cl.ColorScheme, - CreatedAt: time.Unix(cl.CreatedAt, 0), - GHSource: null.NewValue(GHSource{}, false), + WorkspaceID: WorkspaceID(cl.WorkspaceID), + ID: ChangelogID(cl.ID), + Subdomain: Subdomain(cl.Subdomain), + Domain: Domain(cl.Domain), + Title: cl.Title, + Subtitle: cl.Subtitle, + LogoSrc: cl.LogoSrc, + LogoLink: cl.LogoLink, + LogoAlt: cl.LogoAlt, + LogoHeight: cl.LogoHeight, + LogoWidth: cl.LogoWidth, + ColorScheme: cl.ColorScheme, + HidePoweredBy: cl.HidePoweredBy == 1, + CreatedAt: time.Unix(cl.CreatedAt, 0), + GHSource: null.NewValue(GHSource{}, false), } if !source.ID.IsNull() && source.ID.IsValid() && !source.WorkspaceID.IsNull() && source.WorkspaceID.IsValid() { @@ -77,18 +78,19 @@ type sqlite struct { func (s *sqlite) CreateChangelog(ctx context.Context, cl Changelog) (Changelog, error) { c, err := s.q.createChangelog(ctx, createChangelogParams{ - ID: cl.ID.String(), - WorkspaceID: cl.WorkspaceID.String(), - Subdomain: cl.Subdomain.String(), - Domain: cl.Domain.NullString(), - Title: cl.Title, - Subtitle: cl.Subtitle, - LogoSrc: cl.LogoSrc, - LogoLink: cl.LogoLink, - LogoAlt: cl.LogoAlt, - LogoHeight: cl.LogoHeight, - LogoWidth: cl.LogoWidth, - ColorScheme: cl.ColorScheme, + ID: cl.ID.String(), + WorkspaceID: cl.WorkspaceID.String(), + Subdomain: cl.Subdomain.String(), + Domain: cl.Domain.NullString(), + Title: cl.Title, + Subtitle: cl.Subtitle, + LogoSrc: cl.LogoSrc, + LogoLink: cl.LogoLink, + LogoAlt: cl.LogoAlt, + LogoHeight: cl.LogoHeight, + LogoWidth: cl.LogoWidth, + ColorScheme: cl.ColorScheme, + HidePoweredBy: boolToInt(cl.HidePoweredBy), }) if err != nil { return Changelog{}, formatUnqueConstraint(err) @@ -146,12 +148,33 @@ func (s *sqlite) ListChangelogs(ctx context.Context, wID WorkspaceID) ([]Changel return res, nil } +// dereferences b to it's int representation +func saveDerefToInt(b *bool) int64 { + if b != nil && *b { + return 1 + } + return 0 +} + +// Returns 1 if b is true, otherwise 2 +func boolToInt(b bool) int64 { + var i int64 + if b { + i = 1 + } + return i +} + func (s *sqlite) UpdateChangelog(ctx context.Context, wID WorkspaceID, cID ChangelogID, args UpdateChangelogArgs) (Changelog, error) { // does not update string fields if they are zero value c, err := s.q.updateChangelog(ctx, updateChangelogParams{ - ID: cID.String(), - WorkspaceID: wID.String(), - Subdomain: args.Subdomain, + ID: cID.String(), + WorkspaceID: wID.String(), + Subdomain: args.Subdomain, + HidePoweredBy: sql.NullInt64{ // update if value != nil + Int64: saveDerefToInt(args.HidePoweredBy), + Valid: args.HidePoweredBy != nil, + }, ColorScheme: args.ColorScheme, SetColorScheme: int(args.ColorScheme) != 0, Title: args.Title, diff --git a/internal/store/store.go b/internal/store/store.go index 0d19e20..ca5a148 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -11,21 +11,22 @@ import ( ) type Changelog struct { - WorkspaceID WorkspaceID - ID ChangelogID - Subdomain Subdomain - Domain Domain - Title apitypes.NullString - Subtitle apitypes.NullString - LogoSrc apitypes.NullString - LogoLink apitypes.NullString - LogoAlt apitypes.NullString - LogoHeight apitypes.NullString - LogoWidth apitypes.NullString - ColorScheme ColorScheme - CreatedAt time.Time - GHSource null.Value[GHSource] - LocalSource null.Value[LocalSource] + WorkspaceID WorkspaceID + ID ChangelogID + Subdomain Subdomain + Domain Domain + Title apitypes.NullString + Subtitle apitypes.NullString + LogoSrc apitypes.NullString + LogoLink apitypes.NullString + LogoAlt apitypes.NullString + LogoHeight apitypes.NullString + LogoWidth apitypes.NullString + ColorScheme ColorScheme + HidePoweredBy bool + CreatedAt time.Time + GHSource null.Value[GHSource] + LocalSource null.Value[LocalSource] } type Workspace struct { @@ -48,16 +49,17 @@ type LocalSource struct { } type UpdateChangelogArgs struct { - Title apitypes.NullString - Subdomain apitypes.NullString - Domain Domain - Subtitle apitypes.NullString - LogoSrc apitypes.NullString - LogoLink apitypes.NullString - LogoAlt apitypes.NullString - LogoHeight apitypes.NullString - LogoWidth apitypes.NullString - ColorScheme ColorScheme + Title apitypes.NullString + Subdomain apitypes.NullString + Domain Domain + Subtitle apitypes.NullString + LogoSrc apitypes.NullString + LogoLink apitypes.NullString + LogoAlt apitypes.NullString + LogoHeight apitypes.NullString + LogoWidth apitypes.NullString + ColorScheme ColorScheme + HidePoweredBy *bool } type Store interface { diff --git a/migrations/20240919193315_changelog_hide_powered_by.sql b/migrations/20240919193315_changelog_hide_powered_by.sql new file mode 100644 index 0000000..816028e --- /dev/null +++ b/migrations/20240919193315_changelog_hide_powered_by.sql @@ -0,0 +1,9 @@ +-- +goose Up +-- +goose StatementBegin +ALTER TABLE changelogs ADD hide_powered_by INTEGER NOT NULL DEFAULT 0 check (hide_powered_by in (0, 1)); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +ALTER TABLE changelogs DROP hide_powered_by; +-- +goose StatementEnd diff --git a/openchangelog.example.yml b/openchangelog.example.yml index 18eccc2..ce18a99 100644 --- a/openchangelog.example.yml +++ b/openchangelog.example.yml @@ -15,6 +15,7 @@ page: title: Changelog subtitle: The latest product updates from Openchangelog colorScheme: system + hidePoweredBy: false logo: src: https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png width: 70px diff --git a/render/renderer.go b/render/renderer.go index 5cb4ca2..1304633 100644 --- a/render/renderer.go +++ b/render/renderer.go @@ -104,6 +104,9 @@ func (r *renderer) RenderIndex(ctx context.Context, w io.Writer, args RenderInde Articles: articles, NextPageURL: nextPageURL, }, + FooterArgs: components.FooterArgs{ + HidePoweredBy: args.CL.HidePoweredBy, + }, }).Render(ctx, w) }