Skip to content

Releases: maddalax/htmgo

V1.0.5

16 Nov 14:57
Compare
Choose a tag to compare

Small release this time, added 2 new JS commands

  1. h.OnRunInterval(time.Duration, commands ... Command)
    Allows you to run JS commands on the specific interval

Usage:

h.Div(
    h.OnLoad(
	  h.RunOnInterval(time.Second,
            js.ConsoleLog("this runs a command every 1s"),
            js.SetInnerHtml(h.Div()),
            js.EvalJs(`alert('you can eval complex js too')`)
	  ),
    )
)
  1. h.RunAfterTimeout(time.Duration, commands ... Command)
    Allows you to run JS commands after a specified timeout
h.Div(
    h.OnLoad(
	  h.RunAfterTimeout(time.Second,
            js.ConsoleLog("this runs a command once after a second"),
            js.SetInnerHtml(h.Div()),
	  ),
    )
)

Additional fixes:
Fixes issues where using simple commands such as js.SetText, js.SetInnerHtml were failing when used in things such as Js.EvalCommands due to 'this' not existing. Commands will now always use 'self' if it exists, otherwise 'this'

1.0.4 - Alpha Websocket Support, Partials can be anywhere

11 Nov 15:33
Compare
Choose a tag to compare

Release 1.0.4

Partial Updates
Partials can now be placed anywhere in the project instead of just the partials folder. This is useful for colocating partials and pages together for a simpler setup. The partials folder is no longer necessary.

As long as your function returns *h.Partial, it will get registered as a partial and can be used, anywhere in the project.

To get these partial updates, make sure to update the htmgo cli.

Mac / Linux

GOPROXY=direct go install github.com/maddalax/htmgo/cli/htmgo@latest

Windows

set GOPROXY=direct && go install github.com/maddalax/htmgo/cli/htmgo@latest

Websocket Extension Alpha
I released alpha support for a websocket extension. Sample project: https://github.com/maddalax/htmgo/tree/master/examples/ws-example. No documentation is written for it at the moment while I'm testing it out on a larger project I'm building with htmgo. Feel free to look through the ws-example project though.

The extension allows you to push elements to the page and react to events such as OnClick, using ws.OnClick. The ws extension handles all of the connection management for you, so you can focus on your core logic.

At the moment the there is no documentation for it and things may not work, so unless you are OK with diving deep into the code, I wouldn't utilize it at this moment.

Here is some load testing I did with it though, results look promising, around 50k sockets connected in a machine with 8gb RAM, sending messages every 1s.
384976066-dbc03621-81cd-4f72-b6d4-c22454074042

PAAS (htmgo project)
Screenshot 2024-11-11 at 9 28 14 AM

Very very early preview, but I'm working on building a larger project with htmgo + the websocket extension. The project will be a dead simple self hosted platform as a service to deploy your apps.

This will be a great test to prove how well htmgo can work for non trivial apps, as well as will be something useful for me personally, and hopefully others.

1.0.3

01 Nov 10:14
Compare
Choose a tag to compare

Discord

htmgo has a new discord, check it out at https://htmgo.dev/discord

New Features

Paths to assets are now included in codegen: #68

Added new JS methods:

  • ToggleText
  • ToggleTextOnParent
  • ToggleTextOnSibling
  • ToggleTextOnChildren
  • ToggleClassOnChildren
  • ToggleClassOnParent
  • ToggleClassOnSibling
  • EvalCommandsOnSelector
  • EvalCommands

Bug Fixes

  1. Fixed issue where the scripts were being appended as children to the wrong parent element (does not affect which element executes them, but could cause an issue if they were appended to a child and that child was removed)
  2. Title tag has been added to the starter template in the header

Eval Commands

A new feature htmgo now supports is being able to run js commands against an element by its reference. See https://htmgo.dev/docs/interactivity/eval-commands for more information

Docs / Examples Redesign

https://htmgo.dev/docs have been redesigned to be split over multiple pages, I'm also working on adding more documentation.
https://htmgo.dev/examples has been overhauled as well, working on adding as many examples as I can.

Thanks all!

v1.0.2 - windows fixes

28 Oct 19:10
Compare
Choose a tag to compare

Nothing too major this release, just the following:

  1. Various windows fixes. I've got a parallels desktop setup now so I can do much better thorough testing on windows (I primarily only use Mac)
  2. Added charset=utf-8 header when rendering the page
  3. Reworking the examples page: https://htmgo.dev/examples, planning to add many more examples

For the windows fixes, they were primarily htmgo cli related, so you'll need to run the following to get the latest CLI updates.

set GOPROXY=direct && go install github.com/maddalax/htmgo/cli/htmgo@latest

v1.0.1

25 Oct 12:26
Compare
Choose a tag to compare

What's Changed

  • feat:add ClassF by @himynamej in #31
  • feat: add Details & Summary tag
  • feat: html to go code formatter now has much better formatting
  • feat: service loader now supports 'transient' 21ac153 for services that need to be recreated every time it is requested, unlike a singleton which is only created once.
  • feat: added a new formatter for htmgo blocks: https://htmgo.dev/docs#miscellaneous-htmgo-format

Bug Fixes

  • Use path by @himynamej in #32
  • Use filename instead of absolute path when filtering to replacing old template name with new one by @Sardonyx001 in #42
  • htmgo run will now prepare the project properly so you don't have to run htmgo build before (#40)

html to go formatter updates:

htmgo offers a way to convert raw html to htmgo code here: https://htmgo.dev/html-to-go

previously the output was not formatted very well, if you pasted in a large block of html, the output you would get looked something like:

func MyComponent() *h.Element {
	return h.Body(
		h.Div(h.Class("flex h-screen flex-col justify-between border-e bg-white"),
			h.Div(h.Class("px-4 py-6"),
				h.Span(h.Class("grid h-10 w-32 place-content-center rounded-lg bg-gray-100 text-xs text-gray-600"), h.Text("Logo")),
				h.Ul(h.Class("mt-6 space-y-1"),
					h.Li(
						h.A(h.Href("#"), h.Class("block rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700"), h.Text("General")),
					),
					h.Li(
						h.Details(h.Class("group [&_summary::-webkit-details-marker]:hidden"),
							h.Summary(h.Class("flex cursor-pointer items-center justify-between rounded-lg px-4 py-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
								h.Span(h.Class("text-sm font-medium"), h.Text("Teams")),
								h.Span(h.Class("shrink-0 transition duration-300 group-open:-rotate-180"),
									h.Svg(h.Attribute("xmlns", "http://www.w3.org/2000/svg"), h.Class("size-5"), h.Attribute("viewBox", "0 0 20 20"), h.Attribute("fill", "currentColor"),
										h.Tag("path", h.Attribute("fill-rule", "evenodd"), h.Attribute("d", "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"), h.Attribute("clip-rule", "evenodd")),
									),
								),
							),
							h.Ul(h.Class("mt-2 space-y-1 px-4"),
								h.Li(
									h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Banned Users")),
								),
								h.Li(
									h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Calendar")),
								),
							),
						),
					),
					h.Li(
						h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Billing")),
					),
					h.Li(
						h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Invoices")),
					),
					h.Li(
						h.Details(h.Class("group [&_summary::-webkit-details-marker]:hidden"),
							h.Summary(h.Class("flex cursor-pointer items-center justify-between rounded-lg px-4 py-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
								h.Span(h.Class("text-sm font-medium"), h.Text("Account")),
								h.Span(h.Class("shrink-0 transition duration-300 group-open:-rotate-180"),
									h.Svg(h.Attribute("xmlns", "http://www.w3.org/2000/svg"), h.Class("size-5"), h.Attribute("viewBox", "0 0 20 20"), h.Attribute("fill", "currentColor"),
										h.Tag("path", h.Attribute("fill-rule", "evenodd"), h.Attribute("d", "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"), h.Attribute("clip-rule", "evenodd")),
									),
								),
							),
							h.Ul(h.Class("mt-2 space-y-1 px-4"),
								h.Li(
									h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Details")),
								),
								h.Li(
									h.A(h.Href("#"), h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"), h.Text("Security")),
								),
								h.Li(
									h.Form(h.Action("#"),
										h.Button(h.Type("submit"), h.Class("w-full rounded-lg px-4 py-2 text-sm font-medium text-gray-500 [text-align:_inherit] hover:bg-gray-100 hover:text-gray-700"), h.Text("Logout")),
									),
								),
							),
						),
					),
				),
			),
			h.Div(h.Class("sticky inset-x-0 bottom-0 border-t border-gray-100"),
				h.A(h.Href("#"), h.Class("flex items-center gap-2 bg-white p-4 hover:bg-gray-50"),
					h.Img(h.Alt(""), h.Src("https://images.unsplash.com/photo-1600486913747-55e5470d6f40?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80"), h.Class("size-10 rounded-full object-cover")),
					h.Div(
						h.P(h.Class("text-xs"),
							h.Strong(h.Class("block font-medium"), h.Text("Eric Frusciante")),
							h.Span(h.Text("[email protected]")),
						),
					),
				),
			),
		),
	)
}

Changes have been made to it so now it formats it properly like so:

func MyComponent() *h.Element {
	return h.Body(
		h.Div(
			h.Class("flex h-screen flex-col justify-between border-e bg-white"),
			h.Div(
				h.Class("px-4 py-6"),
				h.Span(
					h.Class("grid h-10 w-32 place-content-center rounded-lg bg-gray-100 text-xs text-gray-600"),
					h.Text("Logo"),
				),
				h.Ul(
					h.Class("mt-6 space-y-1"),
					h.Li(
						h.A(
							h.Href("#"),
							h.Class("block rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700"),
							h.Text("General"),
						),
					),
					h.Li(
						h.Details(
							h.Class("group [&_summary::-webkit-details-marker]:hidden"),
							h.Summary(
								h.Class("flex cursor-pointer items-center justify-between rounded-lg px-4 py-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
								h.Span(
									h.Class("text-sm font-medium"),
									h.Text("Teams"),
								),
								h.Span(
									h.Class("shrink-0 transition duration-300 group-open:-rotate-180"),
									h.Svg(
										h.Attribute("xmlns",
											"http://www.w3.org/2000/svg",
										),
										h.Class("size-5"),
										h.Attribute("viewBox",
											"0 0 20 20",
										),
										h.Attribute("fill",
											"currentColor",
										),
										h.Tag("path",
											h.Attribute("fill-rule",
												"evenodd",
											),
											h.Attribute("d",
												"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",
											),
											h.Attribute("clip-rule",
												"evenodd",
											),
										),
									),
								),
							),
							h.Ul(
								h.Class("mt-2 space-y-1 px-4"),
								h.Li(
									h.A(
										h.Href("#"),
										h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
										h.Text("Banned Users"),
									),
								),
								h.Li(
									h.A(
										h.Href("#"),
										h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
										h.Text("Calendar"),
									),
								),
							),
						),
					),
					h.Li(
						h.A(
							h.Href("#"),
							h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
							h.Text("Billing"),
						),
					),
					h.Li(
						h.A(
							h.Href("#"),
							h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
							h.Text("Invoices"),
						),
					),
					h.Li(
						h.Details(
							h.Class("group [&_summary::-webkit-details-marker]:hidden"),
							h.Summary(
								h.Class("flex cursor-pointer items-center justify-between rounded-lg px-4 py-2 text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
								h.Span(
									h.Class("text-sm font-medium"),
									h.Text("Account"),
								),
								h.Span(
									h.Class("shrink-0 transition duration-300 group-open:-rotate-180"),
									h.Svg(
										h.Attribute("xmlns",
											"http://www.w3.org/2000/svg",
										),
										h.Class("size-5"),
										h.Attribute("viewBox",
											"0 0 20 20",
										),
										h.Attribute("fill",
											"currentColor",
										),
										h.Tag("path",
											h.Attribute("fill-rule",
												"evenodd",
											),
											h.Attribute("d",
												"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",
											),
											h.Attribute("clip-rule",
												"evenodd",
											),
										),
									),
								),
							),
							h.Ul(
								h.Class("mt-2 space-y-1 px-4"),
								h.Li(
									h.A(
										h.Href("#"),
										h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
										h.Text("Details"),
									),
								),
								h.Li(
									h.A(
										h.Href("#"),
										h.Class("block rounded-lg px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-100 hover:text-gray-700"),
										h.Text("Security"),
									),
								),
								h.Li(
									h.Form(
										h.Action("#"),
										h.Button(
											h.Type("submit"),
											h.Cla...
Read more

v1.0.0 Stable Release

21 Oct 15:43
Compare
Choose a tag to compare

I am excited to announce the stable release of HTMGO v1.0.0! After two months in alpha this release brings the stability and performance enhancements needed for production use.

The API's are now stable and can be considered safe from breaking changes.

Thanks everyone!

v0.3.7

20 Oct 15:28
Compare
Choose a tag to compare
  1. Added a few helper methods to h.RequestContext
// redirect to a specific location
func (c *RequestContext) Redirect(path string, code int)

// set cookie
func (c *RequestContext) SetCookie(cookie *http.Cookie)

func (c *RequestContext) IsHttpPost() bool 
func (c *RequestContext) IsHttpGet() bool
func (c *RequestContext) IsHttpPut() bool 
func (c *RequestContext) IsHttpDelete() bool 
  1. Fixed bug with HxOnMutationError

  2. Added support for returning 'nil' in page or partials. This allows you to write your own response (such as redirecting), without having to return a page or partial, just return nil

Usage:

func IndexPage(ctx *h.RequestContext) *h.Page {
	u, ok := user.GetUserOrRedirect(ctx)
	if !ok {
		return nil
	}
	return h.NewPage(
		RootPage(UserProfilePage(u)),
	)
}
  1. Updated htmx to swap content if the http status code is 400 or 422. It normally does not do this by default but it is important to show error messages in the case of 400's. Generally 400's are from our own code, so we should be to control what gets swapped in and show proper error messages.

  2. Added h.EmptyPage() to return a blank page

v0.3.6

14 Oct 15:22
Compare
Choose a tag to compare

What's Changed

  1. Added a way to configure html via a yaml file (htmgo.yml)

Simply place a htmgo.yml in the root of your project. The following is supported as of now:

# htmgo configuration

# if tailwindcss is enabled, htmgo will automatically compile your tailwind and output it to assets/dist
tailwind: true

# which directories to ignore when watching for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_ignore: [".git", "node_modules", "dist/*"]

# files to watch for changes, supports glob patterns through https://github.com/bmatcuk/doublestar
watch_files: ["**/*.go", "**/*.css", "**/*.md"]

Important: If you already have the htmgo CLI installed, make sure to run GOPROXY=direct go install github.com/maddalax/htmgo/cli/htmgo@latest to install the latest version.

Full Changelog: v0.3.5...v0.3.6

v0.3.5

11 Oct 14:19
Compare
Choose a tag to compare
  1. Updated OnLoad event handler to always be called on any element (not just browser supported ones such as Script, Body, Img, etc)
    This means if you need run a command / eval js against a specific element when it loads in (on initial load or even after swap), you can do so.

Example:

h.Div(
    h.OnLoad(
        js.EvalJs(`self.focus()`)
    )
)

You can reference the current element using 'self' or 'element' if you are evaluating JS on a parent or sibling.

  1. Removed usage of html.EscapeString on GetPartialPathWithQs

v0.3.4

09 Oct 15:42
Compare
Choose a tag to compare

What's Changed

  1. Update watcher to include newly added directories
  2. Fix GetPartialPath 404ing if calling it from a nested route (#17) thanks @Pitasi
  3. Add a few utility methods to h.RequestContext
func (c *RequestContext) FormValue(key string)
func (c *RequestContext) Header(key string)
func (c *RequestContext) UrlParam(key string) 
func (c *RequestContext) QueryParam(key string)
func (c *RequestContext) IsBoosted() bool 
func (c *RequestContext) IsHxRequest() bool
func (c *RequestContext) HxPromptResponse() 
func (c *RequestContext) HxTargetId() 
func (c *RequestContext) HxTriggerName() 
func (c *RequestContext) HxTriggerId() 
func (c *RequestContext) HxCurrentBrowserUrl()

Also added

func GetRequestContext(r *http.Request) *RequestContext {
	return r.Context().Value(RequestContextKey).(*RequestContext)
}

Usage:

app.Router.Get("/test", func(writer http.ResponseWriter, request *http.Request) {
	ctx := h.GetRequestContext(request)
})

For if you need to get the h.RequestContext from an http.Request. This is useful if you are doing your own routing but still want to use h.RequestContext to pass to other things such as partials or other pages.

Full Changelog: v0.3.3...v0.3.4