Reactivity (PulsePoint)
PulsePoint is the default reactive frontend layer for Caspian. In the
current runtime, you author plain HTML plus a plain
<script>
inside a single template root, and Caspian injects the runtime-facing
attributes before the browser mounts the page.
Authored vs Runtime
Authored templates
- Keep exactly one root element or one imported
x-*root. - Use a plain
<script>inside that root when needed. - Use PulsePoint helpers like
pp.state,pp.effect, andpp.rpc. - Do not handwrite
pp-componentortype="text/pp".
Runtime output
- The browser sees mounted roots with
pp-component. - Owned scripts are rewritten to
script[type="text/pp"]. - The global
ppruntime auto-mounts on DOM ready. - Those runtime details explain behavior, but they are not the default authored form.
Hard invariant
A sibling <script>
after the root or any second top-level node breaks Caspian’s component
boundary injection. Keep the script inside the single authored root.
Core APIs
pp.state(initial)
pp.effect(callback, deps?)
pp.layoutEffect(callback, deps?)
pp.ref(initialValue?)
pp.context(token)
pp.portal(ref, target?)
pp-for on <template>
pp-ref
pp-spread / pp-style
<section> <h2>{title}</h2> <p>Count: {count}</p> <button onclick="setCount(count + 1)"> Increment </button> <script> const title = "Counter" = pp.props; const [count, setCount] = pp.state(0); pp.effect(() => console.log(count), [count]); </script> </section>
Browser To Server
Use pp.rpc()
as the default bridge from interactive UI to route or backend
@rpc()
actions. Older wording like pp.fetchFunction()
is outdated for the current bundled runtime.
async function saveProfile() const user = await pp.rpc("update_user", name: "Jeff" ); const file = fileInput.files[0]; await pp.rpc("upload_avatar", avatar: file ); await pp.rpc("search", query , true);
- Redirect headers are honored through
pp.redirect(). - Streaming and upload progress use the options form of
pp.rpc(). - For CRUD views, keep the authoritative list in
pp.state(...)and render it withpp-for.
URL And Navigation
Use native URLSearchParams
when you need to read the current query string. For navigation, use
pp.redirect()
for programmatic moves and keep standard <a>
links for normal route navigation.
const params = new URLSearchParams(window.location.search); const tab = params.get("tab") || "overview";
function goHome() pp.redirect("/dashboard"); function goExternal() pp.redirect("https://google.com");
pp-reset-scroll="true"
on the main content pane when that pane should reset on child-route
navigation while sidebars or rails keep their scroll position.
Context And Refs
The current context API follows a React-style provider pattern:
pp.createContext(...),
a Context.Provider
tag with a value expression, and
pp.context(token).
Do not invent legacy helpers like pp.provideContext.
<div> <input pp-ref="nameInput" /> <button onclick="nameInput.current?.focus()">Focus</button> <script> const nameInput = pp.ref(null); </script> </div>
For Caspian-specific authoring rules, prefer the installed runtime and the packaged Caspian docs over older examples or third-party snippets.