算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
in expressions\n- Setting a signal to `null` or `undefined` removes it\n- Use dot-notation for nested signals: `$user.name`\n\n### DOM Patching\n\nDatastar uses morphing to update only changed DOM parts while preserving state. The backend sends HTML fragments that patch into the existing page.\n\n## Attributes Reference\n\n### State Management\n\n**data-signals** - Initialize reactive signals:\n```html\n\u003cdiv data-signals=\"{name: 'World', count: 0}\">\u003c/div>\n```\n\n**data-computed** - Create derived read-only signals:\n```html\n\u003cdiv data-computed=\"{doubled: $count * 2}\">\u003c/div>\n```\n\n**data-init** - Run expressions when element loads:\n```html\n\u003cdiv data-init=\"console.log('Loaded')\">\u003c/div>\n```\n\n### Data Binding\n\n**data-text** - Bind text content to an expression:\n```html\n\u003cspan data-text=\"'Hello, ' + $name\">\u003c/span>\n```\n\n**data-bind** - Two-way binding for form elements:\n```html\n\u003cinput data-bind=\"$name\" type=\"text\">\n```\n\n**data-show** - Conditionally show/hide elements:\n```html\n\u003cdiv data-show=\"$isVisible\">Only shown when true\u003c/div>\n```\n\n### Styling\n\n**data-class** - Conditionally apply CSS classes:\n```html\n\u003cdiv data-class=\"{'active': $isActive, 'error': $hasError}\">\u003c/div>\n```\n\n**data-style** - Set inline styles dynamically:\n```html\n\u003cdiv data-style=\"{'color': $textColor, 'opacity': $opacity}\">\u003c/div>\n```\n\n**data-attr** - Set HTML attributes dynamically:\n```html\n\u003cbutton data-attr=\"{'disabled': $isLoading}\">Submit\u003c/button>\n```\n\n### Events\n\n**data-on** - Attach event listeners:\n```html\n\u003cbutton data-on:click=\"$count++\">Click me\u003c/button>\n\u003cinput data-on:input=\"$search = evt.target.value\">\n\u003cform data-on:submit__prevent=\"@post('/submit')\">\n```\n\nThe `evt` variable references the event object.\n\n**data-on-intersect** - Trigger when element enters viewport:\n```html\n\u003cdiv data-on-intersect=\"@get('/load-more')\">Loading...\u003c/div>\n```\n\n**data-on-interval** - Run at regular intervals:\n```html\n\u003cdiv data-on-interval=\"$elapsed++\">Timer: \u003cspan data-text=\"$elapsed\">\u003c/span>\u003c/div>\n```\n\n**data-on-signal-patch** - Execute when signals update:\n```html\n\u003cdiv data-on-signal-patch=\"console.log('Signals changed:', patch)\">\u003c/div>\n```\n\n### DOM Control\n\n**data-ref** - Create signal referencing DOM element:\n```html\n\u003cinput data-ref=\"$inputEl\" type=\"text\">\n```\n\n**data-ignore** - Exclude element from Datastar processing:\n```html\n\u003cdiv data-ignore>Third-party widget here\u003c/div>\n```\n\n**data-ignore-morph** - Keep Datastar active but skip morphing:\n```html\n\u003cdiv data-ignore-morph>Preserve this DOM structure\u003c/div>\n```\n\n**data-preserve-attr** - Preserve attributes during morphing:\n```html\n\u003cinput data-preserve-attr=\"value\" type=\"text\">\n```\n\n### Backend Actions\n\n**@get(), @post(), @put(), @patch(), @delete()** - Send requests to backend:\n```html\n\u003cbutton data-on:click=\"@get('/api/data')\">Load\u003c/button>\n\u003cbutton data-on:click=\"@post('/api/submit')\">Submit\u003c/button>\n```\n\n## Modifiers\n\nModifiers extend attribute behavior using double-underscore syntax:\n\n### Timing Modifiers\n- `__debounce` / `__debounce_500ms` - Debounce execution\n- `__throttle` / `__throttle_1s` - Throttle execution\n- `__delay` / `__delay_200ms` - Delay execution\n\n### Event Modifiers\n- `__prevent` - Call `preventDefault()`\n- `__stop` - Call `stopPropagation()`\n- `__capture` - Use capture phase\n- `__passive` - Mark as passive listener\n- `__once` - Execute only once\n- `__self` - Only trigger if target is the element itself\n- `__outside` - Trigger when event occurs outside element\n- `__window` - Attach listener to window\n\n### Example with Modifiers\n```html\n\u003cinput data-on:input__debounce_300ms=\"@get('/search?q=' + $query)\">\n\u003cbutton data-on:click__once=\"@post('/track-click')\">Track\u003c/button>\n\u003cform data-on:submit__prevent=\"@post('/submit')\">\n```\n\n## Server-Sent Events (SSE)\n\nDatastar uses SSE for streaming responses. The backend sends events with `text/event-stream` content type.\n\n### SSE Event Types\n\n**datastar-patch-elements** - Patch HTML into the DOM:\n```\nevent: datastar-patch-elements\ndata: elements \u003cdiv id=\"content\">Updated content\u003c/div>\n```\n\n**datastar-patch-signals** - Update signal values:\n```\nevent: datastar-patch-signals\ndata: signals {count: 42}\n```\n\n**datastar-remove-elements** - Remove elements by selector:\n```\nevent: datastar-remove-elements\ndata: selector #old-element\n```\n\n---\n\n# Part 2: gomponents-datastar\n\n## Overview\n\ngomponents-datastar provides Go functions that generate Datastar attributes as gomponents nodes. It integrates seamlessly with the gomponents library.\n\n## Import Convention\n\nUse dot imports for a clean DSL:\n\n```go\nimport (\n . \"maragu.dev/gomponents\"\n . \"maragu.dev/gomponents/html\"\n data \"maragu.dev/gomponents-datastar\"\n)\n```\n\nNote: The `data` alias is recommended for datastar.\n\nFor up-to-date API documentation, run:\n```\ngo doc maragu.dev/gomponents-datastar\n```\n\n## Function Reference\n\n### State Management\n\n**Signals** - Initialize reactive signals:\n```go\ndata.Signals(map[string]any{\n \"count\": 0,\n \"name\": \"World\",\n})\n```\n\n**Computed** - Create computed signals (key-value pairs):\n```go\ndata.Computed(\"doubled\", \"$count * 2\")\n```\n\n**Init** - Run expression on load:\n```go\ndata.Init(\"console.log('Component loaded')\")\n```\n\n### Data Binding\n\n**Text** - Bind text content:\n```go\nSpan(data.Text(\"'Hello, ' + $name\"))\n```\n\n**Bind** - Two-way form binding:\n```go\nInput(Type(\"text\"), data.Bind(\"$name\"))\n```\n\n**Show** - Conditional visibility:\n```go\nDiv(data.Show(\"$isVisible\"), Text(\"Shown when visible\"))\n```\n\n### Styling\n\n**Class** - Conditional classes (key-value pairs):\n```go\ndata.Class(\"active\", \"$isActive\", \"error\", \"$hasError\")\n```\n\n**Style** - Dynamic inline styles (key-value pairs):\n```go\ndata.Style(\"color\", \"$textColor\", \"opacity\", \"$opacity\")\n```\n\n**Attr** - Dynamic attributes (key-value pairs):\n```go\ndata.Attr(\"disabled\", \"$isLoading\", \"aria-busy\", \"$isLoading\")\n```\n\n### Events\n\n**On** - Attach event listeners:\n```go\ndata.On(\"click\", \"$count++\")\ndata.On(\"click\", \"@post('/submit')\", data.ModifierPrevent)\ndata.On(\"input\", \"$search = evt.target.value\", data.ModifierDebounce)\n```\n\n**OnIntersect** - Viewport intersection:\n```go\ndata.OnIntersect(\"@get('/load-more')\")\n```\n\n**OnInterval** - Periodic execution:\n```go\ndata.OnInterval(\"$elapsed++\")\n```\n\n**OnSignalPatch** - React to signal changes:\n```go\ndata.OnSignalPatch(\"console.log('Updated')\")\n```\n\n### DOM Control\n\n**Ref** - Reference DOM element:\n```go\ndata.Ref(\"$inputEl\")\n```\n\n**Ignore** - Skip Datastar processing:\n```go\ndata.Ignore()\n```\n\n**IgnoreMorph** - Skip morphing only:\n```go\ndata.IgnoreMorph()\n```\n\n**PreserveAttr** - Preserve attributes during morph:\n```go\ndata.PreserveAttr(\"value\", \"checked\")\n```\n\n### Request Helpers\n\n**Indicator** - Show loading state:\n```go\ndata.Indicator(\"$isLoading\")\n```\n\n**JSONSignals** - Control which signals are sent:\n```go\ndata.JSONSignals(data.Filter{Include: \"form.*\"})\ndata.JSONSignals(data.Filter{Exclude: \"internal.*\"})\n```\n\n## Modifiers\n\nUse modifier constants with event functions:\n\n```go\n// Timing\ndata.ModifierDebounce // __debounce\ndata.ModifierThrottle // __throttle\ndata.ModifierDelay // __delay\n\n// Event behavior\ndata.ModifierPrevent // __prevent\ndata.ModifierStop // __stop\ndata.ModifierCapture // __capture\ndata.ModifierPassive // __passive\ndata.ModifierOnce // __once\ndata.ModifierSelf // __self\ndata.ModifierOutside // __outside\ndata.ModifierWindow // __window\n\n// Duration/threshold helpers\ndata.Duration(500 * time.Millisecond) // __500ms\ndata.Threshold(0.5) // __threshold_0.5\n```\n\n## Complete Example\n\n```go\npackage views\n\nimport (\n \"net/http\"\n\n . \"maragu.dev/gomponents\"\n . \"maragu.dev/gomponents/html\"\n ghttp \"maragu.dev/gomponents/http\"\n ds \"maragu.dev/gomponents-datastar\"\n)\n\nfunc CounterPage() Node {\n return HTML5(HTML5Props{\n Title: \"Counter\",\n Language: \"en\",\n Head: []Node{\n Script(\n Type(\"module\"),\n Src(\"https://cdn.jsdelivr.net/gh/starfederation/[email protected]/bundles/datastar.js\"),\n ),\n },\n Body: []Node{\n Div(\n data.Signals(map[string]any{\"count\": 0}),\n\n H1(data.Text(\"'Count: ' + $count\")),\n\n Button(\n data.On(\"click\", \"$count++\"),\n Text(\"Increment\"),\n ),\n\n Button(\n data.On(\"click\", \"$count--\"),\n Text(\"Decrement\"),\n ),\n\n Button(\n data.On(\"click\", \"@post('/api/save')\"),\n Text(\"Save to Server\"),\n ),\n ),\n },\n })\n}\n\nfunc SearchForm() Node {\n return Form(\n data.Signals(map[string]any{\"query\": \"\", \"results\": []any{}}),\n data.On(\"submit\", \"@get('/search?q=' + $query)\", data.ModifierPrevent),\n\n Input(\n Type(\"text\"),\n data.Bind(\"$query\"),\n data.On(\"input\", \"@get('/search?q=' + $query)\", data.ModifierDebounce, data.Duration(300*time.Millisecond)),\n Placeholder(\"Search...\"),\n ),\n\n Div(\n ID(\"results\"),\n data.Show(\"$results.length > 0\"),\n data.Text(\"'Found ' + $results.length + ' results'\"),\n ),\n )\n}\n```\n\n## SSE Handler Pattern\n\n```go\nfunc handleSSE(w http.ResponseWriter, r *http.Request) {\n w.Header().Set(\"Content-Type\", \"text/event-stream\")\n w.Header().Set(\"Cache-Control\", \"no-cache\")\n w.Header().Set(\"Connection\", \"keep-alive\")\n\n flusher, ok := w.(http.Flusher)\n if !ok {\n http.Error(w, \"SSE not supported\", http.StatusInternalServerError)\n return\n }\n\n // Patch HTML elements\n fmt.Fprintf(w, \"event: datastar-patch-elements\\n\")\n fmt.Fprintf(w, \"data: elements \u003cdiv id=\\\"content\\\">Updated!\u003c/div>\\n\\n\")\n flusher.Flush()\n\n // Update signals\n fmt.Fprintf(w, \"event: datastar-patch-signals\\n\")\n fmt.Fprintf(w, \"data: signals {\\\"count\\\": 42}\\n\\n\")\n flusher.Flush()\n}\n```\n\n## Tips\n\n1. **Signal naming:** Use `
datastar【设计与创意】Guide for building interactive web UIs with Datastar and gomponents-datastar. Use this skill when adding frontend interactivity to Go web applications with Datastar attributes.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Datastar is a lightweight frontend framework that enables backend-driven, interactive UIs through a hypermedia-first approach. It combines backend reactivity (similar to htmx) with frontend reactivity (like Alpine.js) using standard HTML data-* attributes.
Use this skill when:
Prerequisite: When using Datastar with Go, also use the gomponents skill for HTML component patterns.
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-RC.7/bundles/datastar.js"></script>
go get maragu.dev/gomponents-datastar
Signals are reactive state containers. When a signal's value changes, all dependent expressions automatically update.
<div data-signals="{count: 0}">
<span data-text="$count"></span>
<button data-on:click="$count++">Increment</button>
</div>
$ in expressionsnull or undefined removes it$user.nameDatastar uses morphing to update only changed DOM parts while preserving state. The backend sends HTML fragments that patch into the existing page.
data-signals - Initialize reactive signals:
<div data-signals="{name: 'World', count: 0}"></div>
data-computed - Create derived read-only signals:
<div data-computed="{doubled: $count * 2}"></div>
data-init - Run expressions when element loads:
<div data-init="console.log('Loaded')"></div>
data-text - Bind text content to an expression:
<span data-text="'Hello, ' + $name"></span>
data-bind - Two-way binding for form elements:
<input data-bind="$name" type="text">
data-show - Conditionally show/hide elements:
<div data-show="$isVisible">Only shown when true</div>
data-class - Conditionally apply CSS classes:
<div data-class="{'active': $isActive, 'error': $hasError}"></div>
data-style - Set inline styles dynamically:
<div data-style="{'color': $textColor, 'opacity': $opacity}"></div>
data-attr - Set HTML attributes dynamically:
<button data-attr="{'disabled': $isLoading}">Submit</button>
data-on - Attach event listeners:
<button data-on:click="$count++">Click me</button>
<input data-on:input="$search = evt.target.value">
<form data-on:submit__prevent="@post('/submit')">
The evt variable references the event object.
data-on-intersect - Trigger when element enters viewport:
<div data-on-intersect="@get('/load-more')">Loading...</div>
data-on-interval - Run at regular intervals:
<div data-on-interval="$elapsed++">Timer: <span data-text="$elapsed"></span></div>
data-on-signal-patch - Execute when signals update:
<div data-on-signal-patch="console.log('Signals changed:', patch)"></div>
data-ref - Create signal referencing DOM element:
<input data-ref="$inputEl" type="text">
data-ignore - Exclude element from Datastar processing:
<div data-ignore>Third-party widget here</div>
data-ignore-morph - Keep Datastar active but skip morphing:
<div data-ignore-morph>Preserve this DOM structure</div>
data-preserve-attr - Preserve attributes during morphing:
<input data-preserve-attr="value" type="text">
@get(), @post(), @put(), @patch(), @delete() - Send requests to backend:
<button data-on:click="@get('/api/data')">Load</button>
<button data-on:click="@post('/api/submit')">Submit</button>
Modifiers extend attribute behavior using double-underscore syntax:
__debounce / __debounce_500ms - Debounce execution__throttle / __throttle_1s - Throttle execution__delay / __delay_200ms - Delay execution__prevent - Call preventDefault()__stop - Call stopPropagation()__capture - Use capture phase__passive - Mark as passive listener__once - Execute only once__self - Only trigger if target is the element itself__outside - Trigger when event occurs outside element__window - Attach listener to window<input data-on:input__debounce_300ms="@get('/search?q=' + $query)">
<button data-on:click__once="@post('/track-click')">Track</button>
<form data-on:submit__prevent="@post('/submit')">
Datastar uses SSE for streaming responses. The backend sends events with text/event-stream content type.
datastar-patch-elements - Patch HTML into the DOM:
event: datastar-patch-elements
data: elements <div id="content">Updated content</div>
datastar-patch-signals - Update signal values:
event: datastar-patch-signals
data: signals {count: 42}
datastar-remove-elements - Remove elements by selector:
event: datastar-remove-elements
data: selector #old-element
gomponents-datastar provides Go functions that generate Datastar attributes as gomponents nodes. It integrates seamlessly with the gomponents library.
Use dot imports for a clean DSL:
import (
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
data "maragu.dev/gomponents-datastar"
)
Note: The data alias is recommended for datastar.
For up-to-date API documentation, run:
go doc maragu.dev/gomponents-datastar
Signals - Initialize reactive signals:
data.Signals(map[string]any{
"count": 0,
"name": "World",
})
Computed - Create computed signals (key-value pairs):
data.Computed("doubled", "$count * 2")
Init - Run expression on load:
data.Init("console.log('Component loaded')")
Text - Bind text content:
Span(data.Text("'Hello, ' + $name"))
Bind - Two-way form binding:
Input(Type("text"), data.Bind("$name"))
Show - Conditional visibility:
Div(data.Show("$isVisible"), Text("Shown when visible"))
Class - Conditional classes (key-value pairs):
data.Class("active", "$isActive", "error", "$hasError")
Style - Dynamic inline styles (key-value pairs):
data.Style("color", "$textColor", "opacity", "$opacity")
Attr - Dynamic attributes (key-value pairs):
data.Attr("disabled", "$isLoading", "aria-busy", "$isLoading")
On - Attach event listeners:
data.On("click", "$count++")
data.On("click", "@post('/submit')", data.ModifierPrevent)
data.On("input", "$search = evt.target.value", data.ModifierDebounce)
OnIntersect - Viewport intersection:
data.OnIntersect("@get('/load-more')")
OnInterval - Periodic execution:
data.OnInterval("$elapsed++")
OnSignalPatch - React to signal changes:
data.OnSignalPatch("console.log('Updated')")
Ref - Reference DOM element:
data.Ref("$inputEl")
Ignore - Skip Datastar processing:
data.Ignore()
IgnoreMorph - Skip morphing only:
data.IgnoreMorph()
PreserveAttr - Preserve attributes during morph:
data.PreserveAttr("value", "checked")
Indicator - Show loading state:
data.Indicator("$isLoading")
JSONSignals - Control which signals are sent:
data.JSONSignals(data.Filter{Include: "form.*"})
data.JSONSignals(data.Filter{Exclude: "internal.*"})
Use modifier constants with event functions:
// Timing
data.ModifierDebounce // __debounce
data.ModifierThrottle // __throttle
data.ModifierDelay // __delay
// Event behavior
data.ModifierPrevent // __prevent
data.ModifierStop // __stop
data.ModifierCapture // __capture
data.ModifierPassive // __passive
data.ModifierOnce // __once
data.ModifierSelf // __self
data.ModifierOutside // __outside
data.ModifierWindow // __window
// Duration/threshold helpers
data.Duration(500 * time.Millisecond) // __500ms
data.Threshold(0.5) // __threshold_0.5
package views
import (
"net/http"
. "maragu.dev/gomponents"
. "maragu.dev/gomponents/html"
ghttp "maragu.dev/gomponents/http"
ds "maragu.dev/gomponents-datastar"
)
func CounterPage() Node {
return HTML5(HTML5Props{
Title: "Counter",
Language: "en",
Head: []Node{
Script(
Type("module"),
Src("https://cdn.jsdelivr.net/gh/starfederation/[email protected]/bundles/datastar.js"),
),
},
Body: []Node{
Div(
data.Signals(map[string]any{"count": 0}),
H1(data.Text("'Count: ' + $count")),
Button(
data.On("click", "$count++"),
Text("Increment"),
),
Button(
data.On("click", "$count--"),
Text("Decrement"),
),
Button(
data.On("click", "@post('/api/save')"),
Text("Save to Server"),
),
),
},
})
}
func SearchForm() Node {
return Form(
data.Signals(map[string]any{"query": "", "results": []any{}}),
data.On("submit", "@get('/search?q=' + $query)", data.ModifierPrevent),
Input(
Type("text"),
data.Bind("$query"),
data.On("input", "@get('/search?q=' + $query)", data.ModifierDebounce, data.Duration(300*time.Millisecond)),
Placeholder("Search..."),
),
Div(
ID("results"),
data.Show("$results.length > 0"),
data.Text("'Found ' + $results.length + ' results'"),
),
)
}
func handleSSE(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported", http.StatusInternalServerError)
return
}
// Patch HTML elements
fmt.Fprintf(w, "event: datastar-patch-elements\n")
fmt.Fprintf(w, "data: elements <div id=\"content\">Updated!</div>\n\n")
flusher.Flush()
// Update signals
fmt.Fprintf(w, "event: datastar-patch-signals\n")
fmt.Fprintf(w, "data: signals {\"count\": 42}\n\n")
flusher.Flush()
}
$ prefix in expressions, not in Go codedata.Text() for Datastar text binding, gomponents Text() for static contentdata.On("click", "...", data.ModifierPrevent, data.ModifierOnce)id attributes