Page source development
Most Page work can be completed in Design, Inspector, and AI Command Center without first learning a frontend framework. Use Source only when you need precise state logic, custom events, complex styling, or a reusable Component.
Source technology
Pages (.qp) and Components (.qpc) use Svelte 5 source with standard HTML, CSS, and TypeScript. Design and Source edit the same Page content: after either mode saves, the other mode reads the same result.
Common source capabilities include:
- keeping temporary interface state for the current Page instance with
$state; - handling Page events with TypeScript functions;
- connecting Pages and Components through props, events, or callbacks;
- importing an App Component relatively from a
.qpor.qpcfile; - using public Components from
@theseus/qp-coreand installed packages.
<script lang="ts">
let running = $state(false);
function toggleRunning() {
running = !running;
}
</script>
<button onclick={toggleRunning}>
{running ? "Stop simulation" : "Start simulation"}
</button>
Component roots and permissions
An App-defined .qpc uses @theseus/qp-core/Widget as its only measurable root, receives business values through props, returns user actions through events or callbacks, and forwards class, style, required DOM attributes, requiredRoles, and unauthorized.
An empty role list allows everyone. Multiple roles use any-match semantics, and App Admin always matches. On mismatch, hidden omits the root and disabled keeps it visible but inert. Native HTML elements do not expose these Component permission properties directly. UI visibility never replaces authorization for Object, Query, file, or Safety capabilities.
Import an App Component relatively from .qp or another .qpc. Use the public module path for a package Component; do not copy package internals.
Component catalog metadata
An App Component can declare the description, one category, and multiple tags used by the Palette in its leading @component comment:
<!--
@component Displays and selects the current station's production date.
@category input
@tags form, date-time, selection
@insert <ProductionDate label="Production date" />
-->
@category accepts only application, layout, navigation, action, input, data-display, visualization, feedback, overlay, media, or composition. @tags accepts at most eight comma- or whitespace-separated lowercase kebab-case tags, each no longer than 32 characters. A comment cannot repeat the category annotation, the tags annotation, or a tag. Invalid metadata produces an explicit source-validation error instead of being ignored.
The optional single-line @insert uses ordinary QP/Svelte markup to declare the initial properties, text, and size generated when the Component is dragged from the Palette. It must contain exactly one root Component with the current Component's name and cannot contain <script> or <style>. @insert changes only the initially inserted source; it does not change runtime defaults. Components without @insert continue to use the generic empty instance.
The category controls the Palette's default grouping. Category, tags, description, source, and directory also participate in search. Multiple selections within one filter dimension use OR, while category, source, and tag dimensions combine with AND.
Use App capabilities
Page source uses getApp() for App-level Queries, Variables, navigation, and the current role. getApp().role is always a string: it is Guest when Guest access is enabled and no user is signed in, then becomes the account's configured role after sign-in. External-service addresses and authentication still belong centrally in Settings → Queries, not in multiple Pages. Use @qx/i18n for localized text while maintaining locales and messages under Settings → I18n.
Use CurrentUser when a Page should display or switch the current operator, and keep it outside a region that its own role rules could hide:
<script lang="ts">
import CurrentUser from "@theseus/qp-core/CurrentUser";
import { getApp } from "@qx/app";
const app = getApp();
const isGuest = $derived(app.role === "Guest");
</script>
<CurrentUser />
Direct app.role comparisons are appropriate for presentation. Shared Component permissions still use requiredRoles. CurrentUser manages only App users. It does not switch the Qixin account or runtime-target account.
Engineering boundaries
- Page events coordinate the interface only. Equipment sequences, waits, timeouts, recovery, and safety interlocks belong in an Object, QG, or reviewed Provider.
$statebelongs to the current Page instance and resets after refresh or re-entry. Use an explicit App or Object storage capability for durable data.- An App may use only public modules from installed packages. Do not add external dependencies that are not managed by the project, and do not copy package internals.
- Live markers follow the current document. Problems contains diagnostics for saved project files from the latest Build. Before delivery, save, Build, and accept the result on the real runtime Page or Player.