smartblock/block.go
Alex Dunmow 38d5fdf4b1 Extract smartblock into a standalone wasm plugin repo
Convert the bundled backend/internal/plugins/smartblock package into a
standalone reactor-mode wasm plugin:

- package smartblock -> package main; add wasip1 main.go with
  wasmguest.Serve(Registration).
- Add go.mod (module git.dev.alexdunmow.com/block/smartblock) pinning
  block/core v0.18.2; no replace directives.
- Flesh out plugin.mod (scope=ninja, kind=plugin, categories, tags,
  display_name, description).
- Point web/eslint.config.js at ../../../cms/web/eslint.config.js and
  switch @block-ninja/ui from workspace:* to the ^0.1.0 registry version;
  rebuild web/dist.
- Add .gitignore (*.bnp, *.wasm, web/node_modules).

Behavior unchanged: registers the "smart" block; assets served from
web/dist. Builds to smartblock-1.0.0.bnp; check-safety passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 02:48:09 +08:00

81 lines
1.7 KiB
Go

package main
import (
"context"
"fmt"
"html"
"strings"
)
// SmartBlockFunc renders a Smart Block by substituting {{field}} placeholders with content values.
func SmartBlockFunc(ctx context.Context, content map[string]any) string {
// Get metadata
meta, ok := content["_meta"].(map[string]any)
if !ok {
return renderEmptyState()
}
generations, ok := meta["generations"].([]any)
if !ok || len(generations) == 0 {
return renderEmptyState()
}
currentID, _ := meta["currentGenerationId"].(string)
if currentID == "" {
return renderEmptyState()
}
// Find the current generation
var template string
for _, gen := range generations {
genMap, ok := gen.(map[string]any)
if !ok {
continue
}
if genMap["id"] == currentID {
template, _ = genMap["template"].(string)
break
}
}
if template == "" {
return renderEmptyState()
}
// Replace {{field}} placeholders with content values
result := template
for key, value := range content {
// Skip meta fields
if strings.HasPrefix(key, "_") {
continue
}
placeholder := "{{" + key + "}}"
var valueStr string
switch v := value.(type) {
case string:
valueStr = html.EscapeString(v)
case float64:
valueStr = fmt.Sprintf("%v", v)
case int:
valueStr = fmt.Sprintf("%d", v)
case bool:
valueStr = fmt.Sprintf("%t", v)
default:
valueStr = fmt.Sprintf("%v", v)
}
result = strings.ReplaceAll(result, placeholder, valueStr)
}
return result
}
func renderEmptyState() string {
return `<div class="smart-block-empty p-8 border-2 border-dashed border-gray-300 rounded-lg text-center text-gray-500">
<p class="text-lg font-medium">Smart Block</p>
<p class="text-sm">Use the editor to generate content with AI</p>
</div>`
}