Token导航 LogoToken导航TokenDH.com
研究检索操作浏览器github未标认证来源可访问许可证需确认审计提醒

starhtmlstarhtml 搜索

Agent Skill

starhtml 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

245

周安装

10

GitHub Stars

1

下载量

79
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:starhtml(starhtml 搜索)
来源仓库:https://github.com/renatocaliari/starhtml-skill
仓库路径:skills/starhtml
安装命令:
npx skills add https://github.com/renatocaliari/starhtml-skill --skill starhtml
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/renatocaliari/starhtml-skill --skill starhtml

简介

starhtml 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。

  • 适用于研究检索类任务,如信息搜集、资料筛选和知识整理。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装,需确认权限范围和联网需求。
  • 建议在使用前检查维护状态,避免触发不必要的文件读写或命令执行操作。
  • starhtml 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

StarHTML — Core Skill

StarHTML = Python objects that compile to reactive Datastar HTML.

After generating any component, validate with: starhtml-check <file.py>

If starhtml-check is not installed: ``bash curl -L https://raw.githubusercontent.com/renatocaliari/starhtml-skill/main/starhtml_check.py \ -o /usr/local/bin/starhtml-check && chmod +x /usr/local/bin/starhtml-check ``
UI Components: For production-ready UI, use StarUI — shadcn/ui for Python. See ./reference/starui.md for all 34+ components (Button, Card, Dialog, Table, etc.)
Sub-references (load when needed, same directory as this file): ./reference/starui.md · ./reference/icons.md · ./reference/js.md · ./reference/handlers.md · ./reference/slots.md · ./reference/demos.md Official demos (canonical runnable examples, always from official framework repo): https://raw.githubusercontent.com/banditburai/starHTML/main/web/demos/NN_name.py

The 6 Rules You Must Not Break

R1 — No f-strings in reactive attributes (they become static)

# WRONG — evaluated once in Python, never updates in browser:
data_text=f"Count: {counter}"

# RIGHT — reactive, updates when signal changes:
data_text="Count: " + counter           # for 1-2 signals
data_text=f("Count: {c}", c=counter)    # for 3+ signals
# f() requires: from starhtml.datastar import f

R2 — data_show always needs flash prevention

# WRONG — element flashes visible before JS loads:
Div("modal content", data_show=is_open)

# RIGHT — hidden by default, shown reactively:
Div("modal content", style="display: none", data_show=is_open)
# Alternatives:
Div("modal content", cls="hidden", data_class_hidden=~is_open)
Div("modal content", style="opacity:0;transition:opacity .3s",
    data_style_opacity=is_open.if_("1", "0"))

R3 — Positional arguments BEFORE keyword arguments

# WRONG — SyntaxError at runtime:
Div(cls="container", "Hello World")

# RIGHT:
Div("Hello World", cls="container")

# Rule: content/signals first, then attributes/handlers

R4 — Signal names must be snake_case

# WRONG — will error at runtime:
(myCounter := Signal("myCounter", 0))

# RIGHT:
(my_counter := Signal("my_counter", 0))

R5 — Walrus operator:= must be wrapped in outer parentheses

# WRONG — won't register as positional argument:
name := Signal("name", "")

# RIGHT — works as positional argument in HTML elements:
(name := Signal("name", ""))

R6 — Signals are reactive state references, NOT data containers

# ❌ WRONG — Signals are NOT for storing/accessing data:
todos = Signal("todos", [])
todos.value.append(item)  # .value does NOT exist!
print(len(todos))         # Signals don't support len()!

# ✅ RIGHT — Use plain Python variables for data:
todos_data = []           # Python variable for the actual data
(todos_count := Signal("todos_count", 0))  # Signal for reactive UI state

# Add item to data:
todos_data.append({"id": 1, "text": "Buy milk"})

# Update Signal (syncs to frontend, can be received in backend):
yield signals(todos_count=len(todos_data))

Understanding Signals vs Data

Signals are reactive state references that sync between frontend ($signalName in JS) and backend (Python Signal objects). They are NOT Python containers for data.

Data lives in regular Python variables (lists, dicts, strings, etc.).

Pattern: Python Data + Reactive Signals

# State management with pure Python
todos_data = []      # The actual data (Python list)
next_id = 1          # Counter (Python int)

# Signals for reactive state (frontend + backend)
(todos_count := Signal("todos_count", 0))
(is_loading := Signal("is_loading", False))

@rt("/todos/add", methods=["POST"])
@sse
def add_todo(todo_text: str):
    global next_id

    # 1. Update Python data (always use variables for data)
    todos_data.append({"id": next_id, "text": todo_text})
    next_id += 1

    # 2. Send updated UI to client
    yield elements(render_todo_item(todos_data[-1]), "#todo-list", "append")

    # 3. Update Signals (syncs to frontend AND can be received in backend)
    yield signals(todos_count=len(todos_data), is_loading=False)

Signals Can Flow Both Ways

# Frontend → Backend: Signal as parameter
@rt("/api/search")
@sse
def search(req, query: Signal):  # Receives Signal from frontend
    results = db.search(query)   # Can read Signal in backend
    yield signals(results_count=len(results))

Frontend-Only Signals (optional _ prefix)

# Signal with _ prefix = frontend-only by convention
# (no backend parameter, not received in SSE handlers)
(_animation_state := Signal("_animation_state", "idle"))

# With _ref_only=True, Signal is excluded from data-signals HTML attribute
(_cache := Signal("_cache", {}, _ref_only=True))

⚠️ Common Signal Mistakes

Mistake 1: f-string with Signal — displays $signal_name literal

(thought_count := Signal("thought_count", 5))

# ❌ WRONG — f-string converts Signal to literal string "$thought_count"
Div(f"{thought_count} thoughts captured")  # Shows: "$thought_count thoughts captured"

# ✅ RIGHT — Signal as argument (concatenates value)
Div(thought_count, " thoughts captured")  # Shows: "5 thoughts captured"

# ✅ RIGHT — Reactive attribute (updates dynamically)
Span(data_text=thought_count)

Mistake 2: Signal in Python conditional — always truthy

(is_saving := Signal("is_saving", False))

# ❌ WRONG — Signal object is always truthy in Python
Button("Save" if not is_saving else "Saving...")  # Always shows "Saving..."

# ✅ RIGHT — Use reactive attribute
Button(
    "Save this thought",
    data_text=is_saving.if_("Saving...", "Save this thought")
)

Key points:

  • Never try to read signal.value or use len(signal) — Signals are not containers
  • Store data in Python variables; use Signals for reactive state that syncs UI
  • Signals without _ prefix automatically sync to backend via parameters
  • In SSE: always yield signals(...) at the end to reset state
  • NEVER use f-strings with Signals — use arguments or data_text
  • **NEVER use Signals in Python conditionals — use reactive attributes

Quick Reference

from starhtml import *

# Define reactive state — walrus := always in outer ()
(counter  := Signal("counter", 0))
(name     := Signal("name", ""))
(visible  := Signal("visible", True))

# Reactive attributes
data_show=visible              # show/hide element
data_text=name                 # display signal value as text
data_bind=name                 # two-way binding (inputs)
data_class_active=visible      # toggle class "active"

# Events
data_on_click=counter.add(1)
data_on_input=(search_fn, {"debounce": 300})    # wait 300ms after typing
data_on_scroll=(update_fn, {"throttle": 16})    # max 60fps
data_on_submit=(post("/api/save"), {"prevent": True})

# Signal operations
counter.add(1)                 # increment: $counter + 1
counter.set(0)                 # assign: $counter = 0
visible.toggle()               # flip boolean: !$visible
name.upper()                   # string method: $name.toUpperCase()
count.default(0)               # nullish fallback: $count ?? 0
count.default(0).clamp(0, 99)  # chain with other methods
theme.one_of("light","dark")   # constrain to valid values
sig.then(action)               # conditional execute if truthy

# Value guards (chainable)
status.one_of("draft", "published")     # validate enum
theme.one_of("light", "dark", default="light")

# Logical operators
name & email                   # → $name && $email
~is_visible                    # → !$is_visible
all(a, b, c)                   # → !!$a && !!$b && !!$c  (preferred for 3+)
any(a, b)                      # → $a || $b
age >= 18                      # → $age >= 18

📄 Runnable example: 01_basic_signals.py (fetch from demos URL above)


Conditional Helpers

HelperBehaviorUse for
sig.if_("a", "b")exclusive — one resultsimple true/false choice
match(sig, a="x", default="z")exclusive — maps value to outputvalue-based mapping
switch([(cond, "msg"),...], default="")exclusive — first match winsvalidation chains
collect([(cond, "cls"),...])inclusive — ALL true combinedCSS class building
# EXCLUSIVE — only one result ever returned
data_text=status.if_("Active", "Inactive")

data_attr_class=match(theme,
    light="bg-white text-black",
    dark="bg-gray-900 text-white",
    default="bg-white")

msg = switch([
    (~name, "Name is required"),
    (name.length < 2, "Name too short"),
    (~email.contains("@"), "Invalid email"),
], default="")

# INCLUSIVE — combines ALL true conditions (correct for CSS classes)
data_attr_class=collect([
    (True, "btn"),
    (is_primary, "btn-primary"),
    (is_large, "btn-lg"),
    (is_disabled, "opacity-50 cursor-not-allowed"),
])

# WRONG: using collect() for exclusive logic (use switch or if_ instead)
# WRONG: using switch() to combine CSS classes (use collect instead)

📄 See: 06_control_attributes.py, 25_advanced_toggle_patterns.py, 28_datastar_helpers_showcase.py


Forms and Binding

Form(
    (name  := Signal("name", "")),
    (email := Signal("email", "")),
    (valid := Signal("valid", all(name, email.contains("@")))),

    Input(type="text",  data_bind=name,  placeholder="Name"),
    Span(
        data_text=switch([(~name, "Required"), (name.length < 2, "Too short")],
                         default=""),
        data_show=~name, style="display:none"
    ),

    Input(type="email", data_bind=email, placeholder="Email"),

    Button("Submit",
           data_attr_disabled=~valid,
           type="submit"),

    data_on_submit=(is_valid.then(post("/api/submit", name=name, email=email)),
                    {"prevent": True})
)

📄 See: 03_forms_binding.py


HTTP Actions and SSE

# HTTP actions — pass signals as params, never f-strings
data_on_click=get("/api/data")
data_on_click=post("/api/submit", name=name_sig, email=email_sig)
data_on_click=is_valid.then(post("/api/submit"))         # conditional
data_on_click=delete("/api/item")

# Conditional execution with .then()
data_on_click=is_confirmed.then(delete("/api/item"))     # only if confirmed
data_effect=is_form_complete.then(auto_save)             # side effect

# WRONG — f-string URL is Python-static, signal value not reactive:
data_on_click=get(f"/api/{item_id}")
# RIGHT — pass signal as parameter:
data_on_click=get("/api/item", id=item_id_sig)

⚠️ POST Endpoint Data Parsing (Critical!)

Datastar sends JSON by default, NOT form data.

# ❌ WRONG — Datastar sends JSON, req.form() returns empty!
@rt("/todos", methods=["POST"])
def create(req):
    form = req.form()  # Returns empty dict!
    text = form.get("text", "")  # Always empty
    mood = form.get("mood", "")

# ✅ RIGHT — Parse JSON first (Datastar default)
@rt("/todos", methods=["POST"])
def create(req):
    import json
    data = json.loads(req.body())
    text = data.get("text", "")
    mood = data.get("mood", "")

# ✅ RIGHT — Support both JSON and form data (defensive)
@rt("/todos", methods=["POST"])
def create(req):
    import json
    try:
        data = json.loads(req.body())
        text = data.get("text", "")
        mood = data.get("mood", "")
    except (json.JSONDecodeError, ValueError):
        # Fallback for traditional form submissions
        form = req.form()
        text = form.get("text", "")
        mood = form.get("mood", "")

Why this happens:

  • Datastar uses fetch() with Content-Type: application/json
  • Traditional HTML forms use Content-Type: application/x-www-form-urlencoded
  • StarHTML/StarUI components send JSON by default

Best Practice: Always parse JSON first, optionally fallback to form data for compatibility.

# SSE endpoint — always yield signals() at end to reset client state
@rt("/send", methods=["POST"])
@sse
def send():
    yield signals(is_sending=True)
    yield elements(Div("msg", cls="msg"), "#chat", "append")  # append mode
    yield elements(Div("x", id="chat"), "#chat")              # replace/morph mode
    yield signals(is_sending=False, message="")               # REQUIRED: reset state

# Hypermedia morph rule: returned element id MUST match the target selector
@rt("/partial")
def partial():
    return Div("new content", id="target")   # id="target" matches get("/partial") target

# SSE Best Practices:
# 1. Always yield signals() at end to reset client state
# 2. For replace-mode: preserve id attribute for future targeting
# 3. Use append/prepend for lists, replace for single elements

📄 See: 02_sse_elements.py, 04_live_updates.py, 05_background_tasks.py, 08_routing_patterns.py


Styling

SSR vs Reactive Attributes

Use CaseSSR Needed?Pattern
Toggle single classNodata_class_active=signal
Tailwind special charsNodata_attr_class=signal.if_("hover:bg-blue-500", "")
Show/hide elementsYesstyle="display: none" + data_show=signal
Base + toggle classesYescls="base" + data_class_*
Base + dynamic classesYescls="base" + data_attr_cls=reactive

CSS Classes

# Simple class names (no special characters) → data_class_*
data_class_active=is_active          # adds/removes class "active"
data_class_hidden=~is_visible        # adds/removes class "hidden"

# Special characters (:  /  [  ]) in class names → data_attr_class
# WRONG — colon in keyword name is a Python parse error:
data_class_hover:bg-blue=sig
# RIGHT:
data_attr_class=is_active.if_("hover:bg-blue-500 focus:ring-2", "")
data_attr_class=is_loading.if_("bg-blue-500/50", "bg-blue-500")
data_attr_class=is_custom.if_("bg-[#1da1f2]", "bg-gray-500")

# data_attr_cls vs data_attr_class — DIFFERENT behaviors:
# data_attr_cls   = ADDITIVE — merges with base cls= classes
# data_attr_class = REPLACES — sets the full class attribute

Button("OK",
       cls="btn",                                        # base classes
       data_attr_cls=is_valid.if_("btn-success", "btn-error"))  # additive

Button("OK",
       data_attr_class=collect([(True, "btn"),
                                (is_primary, "btn-primary")]))  # replaces

# Dictionary syntax for conditional classes
data_class={"active selected": role == "admin", "disabled": role == "guest"}

CSS Properties

# Static CSS (SSR)
style="background-color: red; font-size: 16px"

# Reactive CSS properties
data_style_width=progress + "px"
data_style_opacity=is_visible.if_("1", "0")

# CSS template with multiple signals
from starhtml.datastar import f
data_attr_style=f("color: {c}; opacity: {o}", c=theme_color, o=opacity)

Computed Signals and Effects

# Computed Signal — pass expression (not literal) as initial value
# auto-updates whenever dependencies change
(first := Signal("first", ""))
(last  := Signal("last", ""))
(full_name := Signal("full_name", first + " " + last))       # string computed
(is_valid  := Signal("is_valid", all(name, email)))          # boolean computed
(total     := Signal("total", price * quantity))             # math computed

# data_effect — side effects when signals change (assignments, not return values)
data_effect=total.set(price * quantity)
data_effect=[
    total.set(price * quantity),
    tax.set(total * 0.1),
    final.set(total + tax),
]

# Performance: exclude internal-only signals from HTML output
(cache := Signal("cache", {}, _ref_only=True))

📄 See: 27_nested_property_chaining.py


Plugins

Each plugin requires import from starhtml.plugins and registration with the app. Fetch the demo file for the exact integration pattern — demo files are complete, runnable examples.

Base URL for all demos: https://raw.githubusercontent.com/banditburai/starHTML/main/web/demos/

from starhtml.plugins import persist, scroll, resize, drag, canvas, position, motion, markdown, split

app, rt = star_app()
app.register(persist)   # register each plugin you need
app.register(motion)
PluginDemo file(s)What it does
persist09_persist_plugin.pySync signals to localStorage/sessionStorage
scroll10_scroll_plugin.pyTrack scroll position, page progress
resize11_resize_plugin.pyWindow/element resize events
drag12_drag_plugin.py, 16_freeform_drag.pyDrag and drop, sortable lists
canvas17_canvas_plugin.py, 18_canvas_fullpage.py, 29_drawing_canvas.pyCanvas drawing
position20_position_plugin.pyElement positioning
motion23_motion_plugin.py, 24_motion_svg_plugin.pyCSS/SVG animations (enter, exit, hover, in_view)
markdown13_markdown_plugin.pyRender markdown content via data_markdown
katex14_katex_plugin.pyMath / LaTeX rendering
mermaid15_mermaid_plugin.pyDiagram rendering
split21_split_responsive.py, 22_split_universal.pyResizable split panes
nodegraph19_nodegraph_demo.pyNode graph UI

Also fetch: 07_todo_list.py (complete real-world app), 30_debugger_demo.py (debugger tool)

For full demo index with descriptions: see ./reference/demos.md For Icon() component: see ./reference/icons.md For js(), f(), regex(): see ./reference/js.md For plugins API (persist, scroll, resize, drag, canvas, position, motion): see ./reference/handlers.md For slot system: see ./reference/slots.md


Common Errors (and How to Fix Them)

ErrorCauseSolution
Signal has no len() or AttributeError: 'Signal' object has no attribute 'value'Treating Signal as data container (Signals are reactive state, not data)Use Python variables for data: todos_data = [] instead of Signal("todos", [])
signals() takes 0 to 1 positional argumentsPassing positional args to signals()Use kwargs: yield signals(count=1, status="done") not signals(count, status)
Method not found on $signal (JS console)Using plugin attributes without registeringImport and register: app.register(persist)
NameError: name 'xyz' is not definedUsing Signal without walrus := parenthesesWrap in parens: (xyz:= Signal("xyz", 0))
SyntaxError: positional argument follows keyword argumentWrong argument orderContent first, attributes after: Div("text", cls="class")
Displays $signal_name literalf-string with Signal — converts to stringUse arguments: Div(count, " items") or data_text=count
Button always shows wrong textSignal in Python conditional — always truthyUse reactive: data_text=is_saving.if_("Saving...", "Save")
Element flashes before hiding on loadMissing flash preventionAdd style="display:none" with data_show
Form submits and reloads pageMissing {"prevent": True}Add: data_on_submit=(post("/api"), {"prevent": True})
POST returns 400 Bad RequestDatastar sends JSON, not form dataParse JSON: data = json.loads(req.body())
SSE endpoint leaves UI in loading stateMissing yield signals() resetAlways end with: yield signals(loading=False)
Signal value not updating in backend handlerTrying to read signal.valueReceive Signal as parameter: def handler(req, my_sig: Signal)
Direct Datastar importImporting @getdatastar/datastar manuallyRemove it — StarHTML manages Datastar automatically

Checker Tool

The checker is a standalone CLI (zero dependencies, stdlib only) that validates StarHTML components.

Install (one-time)

Check if installed:

starhtml-check --help || echo "Not installed"

Install if missing:

# macOS / Linux - global install (recommended)
curl -L https://raw.githubusercontent.com/renatocaliari/starhtml-skill/main/starhtml_check.py \
  -o /usr/local/bin/starhtml-check && chmod +x /usr/local/bin/starhtml-check

# Or user-local (no sudo required)
curl -L https://raw.githubusercontent.com/renatocaliari/starhtml-skill/main/starhtml_check.py \
  -o ~/.local/bin/starhtml-check && chmod +x ~/.local/bin/starhtml-check

Verify installation:

starhtml-check --version  # Should show help

Updating

Once installed, update to the latest version anytime:

starhtml-check --update

This fetches the latest version from GitHub, creates a backup (.bak), and updates automatically.

Usage

# After generating StarHTML code, always run:
starhtml-check component.py           # full analysis
starhtml-check --summary f.py         # compact output (fewer tokens)
starhtml-check --update               # check for updates and update

Development Loop: write → check → fix ERRORs → re-run → ✓ no issues

Output Levels

  • ERRORS — must fix, will break runtime or reactivity
  • WARNINGS — should fix, may cause subtle bugs or UX issues
  • SUMMARY — signal inventory + total counts

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

36.61%
按下载量换算29

Claude

32.42%
按下载量换算26

Cursor

18.15%
按下载量换算14

Gemini CLI

9.94%
按下载量换算8

安全审计

Gen Agent Trust Hub

可疑

Socket

可疑

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills