Skip to main content
Hugs
A hug has two parts you write: its definition (hug.ts, with define_hug) and its page (src/main.ts, with mount_hug). Frontend authors install one package, @getcliqio/hug-frontend. The tutorial builds one end to end; mount_hug in depth, UI kit, React, Files and links and Testing go further.

The definition — define_hug

hug.ts
  • file({ accept, max_bytes, multiple, optional }) makes a field an upload slot. accept takes MIME types (image/*) and extensions (.pdf); multiple takes up to 10 files. The answer holds a reference ({ artifact_id, name, size, content_type, sha256 }), never the bytes.
  • .describe('…') on a field is its label in the default read-only view.
  • Parameterized hugs — values, files and summary may depend on the params, for an answer whose shape depends on what was asked: one verdict per document asked about; form’s fields, listed in its params. Both checks — the page’s and the agent’s — use the Hug’s own params.
  • Rules across fields go in the schema: superRefine with paths (ctx.addIssue({ path: [i, 'note'], … })), or discriminatedUnion for answers of different shapes (approve or reject).
  • define_hug is pure (Zod only), so the same file runs in the page, in agent code and — compiled to dist/definition.mjs — in cliqd and LLM agents.
The definition’s methods, for tests and agents: check_params(raw), check(raw, params) (both return { ok: true, value } or { ok: false, problems: [{ path, message }] }), values_schema(params), files(params), summary(values, params), describe().

The page — mount_hug

src/main.ts
edit receives: What mount_hug does for you: connects to the Hub page and applies its theme; starts from the person’s draft, else their earlier answer, else initial; checks the params (a bad one shows an error panel); after every set or replace, checks the answer with the definition and tells the Hub whether it’s valid, its problems, the Submit label and the summary; switches between edit and view; keeps the frame’s height fitted. Fields that depend on other fields are plain conditions in edit (it redraws on every change). Clear a field you hide with set(path, undefined), and put the same rule in the definition. html is lit-html: @event=${fn}, .property=${v}, lists with map, nothing for an empty branch.

The UI kit

import '@getcliqio/hug-frontend/ui' registers web components that match the Hub — its font, colours (light and dark) and spacing — and are keyboard-reachable and labelled. form and approval are built only from these. Theme: the kit uses CSS variables the Hub sets — --cliq-bg, --cliq-panel, --cliq-ink, --cliq-ink-2, --cliq-ink-3, --cliq-line, --cliq-acc, --cliq-ok, --cliq-warn, --cliq-bad. Use them in your own CSS too. A page that doesn’t use the kit can still load the Hub’s font from @getcliqio/hug-frontend/ui/fonts.css.

React

cliq hug new <name> --react scaffolds a React page:
src/main.tsx
useHug(definition, { initial, submit }) returns { params, value, set, replace, problems, hug }; <HugRoot> mounts it and <DefaultView> draws the default read-only view.

What a page can’t do

The page runs in a sandboxed frame with no network (Security):

Testing

The definition is plain code — test check, summary and files directly (see the tutorial). For the page, mock_host from @getcliqio/hug-frontend/testing stands in for the Hub:
mock_host({ params, mode, mine, viewing, draft, attachments, upload, read, … }) records everything the page sends (sent, last_values()); upload and read say what the person picks and what a file holds. In the browser: cliq hug dev <dir> [--params p.json] [--attachments dir] [--view answer.json] shows the hug on cliqd’s local Hug page, rebuilding and reloading as you edit, with a panel of what it sends.

Building

cliq hug build runs the hug’s build (npm run build), writes hug.json and definition.mjs, and checks the bundle: text files only (binary assets inlined), every file it loads in the bundle, at most 5 MB and 200 files, hug.json and definition.mjs present. CliqHub checks the same rules when the bundle is uploaded. The scaffolded build.mjs passes hug_esbuild_plugins() from @getcliqio/hug-frontend/build to esbuild, for both the page and definition.mjs. Today it keeps only zod’s English error messages — import { z } from 'zod' would otherwise bring about 40 languages, some 500 KB, into each. Keep it in your build so later build fixes reach your hug too; leave it out only if your hug switches zod to another language.