1package craft23import (4 "net/url"5 "strconv"6 "strings"7)89func Render(iURL string) string {10 u, err := url.Parse(iURL)11 if err != nil {12 return renderRecipeError("Invalid URL")13 }1415 switch u.Path {16 case "":17 return renderRecipeHome()18 case "recipe":19 return renderRecipeResult(u.Query().Get("recipe"))20 case "recipes":21 return renderRecipeList(u.Query().Get("page"))22 default:23 return renderRecipeError("Not found")24 }25}2627func renderRecipeHome() string {28 return `# AKKADIA Recipe2930Ingredients are bound to occupied occupancy bits from slot 0 upward.3132Current craft fee is ` + strconv.FormatInt(GetCraftFee(), 10) + `ugnot.3334Enter a recipe to find its item ID.3536<gno-form path="recipe">37 <gno-input name="recipe" placeholder="0013|iron:1|iron:2|stick:3" />38</gno-form>3940* [Browse Recipes](./craft:recipes)41`42}4344func renderRecipeList(rawPage string) string {45 page := 146 if rawPage != "" {47 parsed, err := strconv.Atoi(rawPage)48 if err != nil || parsed < 1 {49 return renderRecipeError("Invalid page")50 }51 page = parsed52 }5354 count := 2055 if count > listLimit {56 count = listLimit57 }5859 output := `# Recipes6061[← Back](./craft:)6263`64 total := GetRecipeCount()65 if total == 0 {66 return output + `*No recipes stored yet.*67`68 }6970 rows := ListRecipes(page, count)71 if len(rows) == 0 {72 return renderRecipeError("Invalid page")73 }74 for _, row := range rows {75 output += "* " + formatRecipeCode(row["recipe"]) + " → " + row["itemId"] + "\n"76 }77 return output78}7980func renderRecipeResult(recipe string) string {81 if recipe == "" {82 return renderRecipeError("Recipe is required")83 }8485 canonical, _, errMsg := parseRecipe(recipe)86 if errMsg != "" {87 return renderRecipeError("Invalid recipe")88 }89 if !recipes.Has(canonical) {90 return renderRecipeError("Recipe not found")91 }9293 return `# Recipe Result9495* **Recipe**: ` + formatRecipeCode(canonical) + `96* **Item ID**: ` + strconv.FormatUint(uint64(mustGetRecipeItemID(canonical)), 10) + `9798[← Back](./craft:)99`100}101102func renderRecipeError(message string) string {103 return `# Error104105> ` + message + `106107[← Back](./craft:)108`109}110111// Recipes use | and - as syntax. A code span keeps those characters112// literal so gnoweb does not show backslash escapes.113func formatRecipeCode(recipe string) string {114 recipe = strings.ReplaceAll(recipe, "\n", " ")115 fence := "`"116 for strings.Contains(recipe, fence) {117 fence += "`"118 }119 return fence + recipe + fence120}121Craft(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string) uint64
DeleteRecipe(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string)
DeleteRecipes(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipesCSV string)
GetCraftFee() int64
GetListLimit() int
GetRecipe(recipe string) uint32
GetRecipeCount() int
HasRecipe(recipe string) bool
ListRecipes(page int, count int) []map[string]string
Render(iURL string) string
SetCraftFee(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, fee int64)
SetListLimit(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, limit int)
SetRecipe(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string, itemID uint32)
SetRecipes(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipesCSV string, itemIDsCSV string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.