Repository navigation
Snap Hints
Hints make a Snap card better:
its name, the columns of a list, the words the assistant says, the numbers of Home with their actions, what Home
invites to browse, when an action applies. Cards work without any: each is built from what its capability declares.
The hints live in one file the app owns, application/config/snap.php, keyed by capability name. Since 2.23.0.
/caligrafy-quill:assistant
Claude writes application/config/snap.php for your capabilities: about, titles, columns, the numbers of Home with
their actions, what Home invites to browse, the card views.
Check
-
php caligrafer.php snapshows[ok] Card hints. - Each key you see in the file is in the table below: a key the framework does not know is ignored silently.
php caligrafer.php snap install writes a starter file when there is none. A complete one:
<?php
return array(
'about' => 'a library app: the books of a catalog, their authors, and the books lent to people',
'cards' => array(
'list_books' => array(
'title' => 'The catalog',
'columns' => array('title', 'author.name', 'published_year'),
'empty' => 'No book yet.',
'insight' => array('label' => 'New this month', 'since' => array('field' => 'created_at', 'days' => 30),
'action' => array('capability' => 'list_books', 'text' => 'Browse the catalog')),
'discover' => array('open' => 'get_book', 'key' => 'id', 'title' => 'title', 'meta' => array('author.name', 'published_year'),
'new' => 'created_at', 'related' => 'author.id', 'opened' => array('get_book' => 'id'),
'unavailable' => array('capability' => 'list_loans', 'field' => 'book_id'), 'available_label' => 'On the shelf',
'title_section' => 'On the shelf for you'),
),
'lend_book' => array('done' => 'Done! {heading} is lent. Enjoy the read.',
'applies' => array('not_in' => array('capability' => 'list_loans', 'field' => 'book_id'))),
'return_book' => array('applies' => array('in' => array('capability' => 'list_loans', 'field' => 'book_id'))),
'reading_report' => array('working' => array('Counting the books of each author…', 'Putting the report together…')),
),
);A file that returns the cards alone (no 'cards' key) works too. about (and APP_NAME in .env) tells the
assistant what it is the assistant of.
| Key | What it does | Page |
|---|---|---|
title, lead, submit, icon
|
the card's name, its line, its button, its icon (Lucide names) | Ask |
labels, icons, hide
|
the labels and icons of fields, the fields left out | Ask |
values |
the labels of fixed values: array('weekday' => array(1 => 'Monday', ...)) makes a select |
Ask |
choices |
where a field's choices come from: array('author_id' => 'list_authors'), or array('capability' => ..., 'value' => 'id', 'label' => 'name')
|
Ask |
current |
the capability an update_ form reads its current values from (get_<thing> by default) |
Ask |
confirm, consequence
|
a Confirm's question, the line under it | Ask |
undo |
what undoes it: array('capability' => 'return_book', 'map' => array('book_id' => 'book_id')) (each input of the undo, from an input or the result of the change), and its button label (Undo) |
Ask |
columns, noun, variant
|
a list's columns, what its rows are ("authors"), its variant (rows) |
Show |
open |
which capability a list's row opens: array('capability' => 'get_book', 'input' => 'id', 'from' => 'book_id')
|
Show |
full |
the page that shows the same thing in full: '/books/{id}'
|
|
empty, empty_next
|
what an empty list says; what would fill it: array('lend_book')
|
Show |
applies |
when an action can be done to a record: in or not_in a list |
Show |
done |
the assistant's line once done; {heading} is what it was about |
|
next |
the capabilities it leads to (suggestions after it) | |
alert |
an alert on Home when rows carry a flag | Offer |
insight |
a number on Home, only with the action it calls for | below |
discover |
the things Home invites to browse | below |
working, async
|
what a background task says while it runs; async without the attribute | Track |
card, view, design
|
its card view, the page whose data-snap part shows it, how its data is designed |
Card views |
'insight' => array('label' => 'New this month', 'icon' => 'sparkles',
'action' => array('capability' => 'list_books', 'text' => 'Browse the catalog'),
'since' => array('field' => 'created_at', 'days' => 30), // count only what was added lately
'ring' => array('of' => 'list_books', 'label' => 'of the catalog', 'invert' => false)), // and a share of another countOne number per read capability with the hint, shown only when there is something to act on (a count above zero) and
the person may take its action. Without an action it is never shown (G157).
On a list capability, Home shows picks of what the app is about, each saying why it is there (6 per list at most, a different few each day):
-
for you: more like what this person opened (only with noticing on), sharing
related; -
new: up to 2 of what was added in the last 30 days, by the field
new; -
the rule: only what is available now, not in the
unavailablelist (available_labelsays it).
| Key | What it is |
|---|---|
open, key
|
how an object opens: the capability, and the field it takes |
title, meta
|
what a pick shows |
new |
the field that says when it was added |
related |
what "more like it" shares (author.id) |
opened |
the requests that mean "looked at it": array('get_book' => 'id', 'lend_book' => 'book_id')
|
unavailable, available_label
|
the business rule: array('capability' => 'list_loans', 'field' => 'book_id')
|
title_section |
the section's title |
-
Hints on
#[Capability]. They are keys ofapplication/config/snap.php; onlyasyncis also a flag of the attribute (G157). - A key with a typo is ignored silently: copy the shapes above.
-
The file changed and nothing did: in a long process (the queue worker, a test) the file is read once;
Caligrafy\Snap\Hints::forget()reads it again.
Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.
Why Caligrafy: the benefits, the measurements, and what it is not yet
-
Quickstart with Claude: describe it, approve it, ship it
or the Quickstart: the same three steps by hand, in 5 minutes
- How Caligrafy works: the ideas in 5 minutes
- Your first capability: build a feature, page included
-
Capabilities in depth
-
#[Capability]: every option - Scheduled jobs: capabilities on a timer
- Notifications: web push and email
- Background jobs: capabilities run later
- Migrations: database changes, in order
- The cache
-
Testing your app
- Dependency injection
- Validation rules
-
Web pages
- Views and page components
- Design styles: another look, one setting
- Declarative routes: every request, in one line
-
Route::page·Route::form·Route::auth·Route::webhook·Route::download·Route::redirect·Route::action·Route::snap· capability routes - How to export a list as CSV
- Controllers: moving one to a declared route · how to, step by step
- Forms and helpers
-
Vue apps
-
Mobile and tablet
-
REST API
-
AI agents
-
Snap Cards and the assistant
- Payments: Stripe and crypto
- Face detection and face login
- OpenAI: chat and images · Assistants
- Open models: Llama, Mistral...
- Chatbots with Watson
- Machine learning in the browser
- Metadata and social cards · search and analytics
-
Putting it online: the production image, the database at every start
-
API reference: every class and method
-
Routing ·
Route·AuthRoutes· pages ·Controller· capabilities -
Data ·
Model·Query· sign-in · jobs ·Notify· payments · AI -
Interfaces · Snap ·
Snap\Cards·Snap\Conversation· testing · tooling · helper functions
-
Routing ·
-
Caligrafer: every command
- Project · build · data · agents · jobs and tests · maintenance · deploy
- Installation, configuration and Caligrafer
- Routing · Request · Controllers · Views
- Vue: flow of information · routes · validations
- Security and upgrading
- Release notes: 2.26 · 2.25 · 2.24 · 2.23 · 2.22 · 2.21 · 2.20 · 2.19 · 2.18 · 2.17 · 2.16 · 2.15 · 2.14 · 2.13 · 2.12 · 2.11 · 2.10 · 2.9 · 2.8 · 2.7 · 2.6 · 2.5 · 2.4 · 2.3 · 2.1 · October 2026 · changelog
- Testing Caligrafy (contributors)