@pipelex/mthds-form 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brand/index.d.ts +21 -104
- package/dist/brand/index.js +9 -382
- package/dist/brand/index.js.map +1 -1
- package/dist/{chunk-ZX52CQJ4.js → chunk-2UCH4AAO.js} +3 -3
- package/dist/{chunk-ZX52CQJ4.js.map → chunk-2UCH4AAO.js.map} +1 -1
- package/dist/{chunk-RHX7LQRL.js → chunk-ENK5QTUK.js} +390 -128
- package/dist/chunk-ENK5QTUK.js.map +1 -0
- package/dist/{chunk-ZB4RYUKV.js → chunk-HVWE3TTC.js} +3 -3
- package/dist/{chunk-ZB4RYUKV.js.map → chunk-HVWE3TTC.js.map} +1 -1
- package/dist/chunk-UZNRJWN5.js +721 -0
- package/dist/chunk-UZNRJWN5.js.map +1 -0
- package/dist/{chunk-IBBAJXIO.js → chunk-XVGAQ4RK.js} +4 -4
- package/dist/{chunk-IBBAJXIO.js.map → chunk-XVGAQ4RK.js.map} +1 -1
- package/dist/core/derive.js +2 -2
- package/dist/core/gate.js +3 -3
- package/dist/core/index.js +12 -12
- package/dist/core/values.js +3 -3
- package/dist/generative/index.d.ts +474 -79
- package/dist/generative/index.js +1261 -512
- package/dist/generative/index.js.map +1 -1
- package/dist/layout-design.mthds +133 -6
- package/dist/react/index.d.ts +24 -6
- package/dist/react/index.js +7 -7
- package/dist/styles-BoXPBjF4.d.ts +258 -0
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-QFX332LE.js +0 -22
- package/dist/chunk-QFX332LE.js.map +0 -1
- package/dist/chunk-RHX7LQRL.js.map +0 -1
- package/dist/manifest-DUeAWeqa.d.ts +0 -17
package/dist/layout-design.mthds
CHANGED
|
@@ -29,6 +29,14 @@ main_pipe = "design_layout"
|
|
|
29
29
|
# - `emit_page` builds exactly that page: it is handed the plan, the catalog
|
|
30
30
|
# with every props signature, and the brief, and emits the JSONL patches.
|
|
31
31
|
#
|
|
32
|
+
# A second entry, `repair_layout`, takes a page the host's own checks refused
|
|
33
|
+
# - the JSONL as emitted and the problems in words - lays the brief out the same
|
|
34
|
+
# way and has `emit_repair` emit the whole page again with those problems
|
|
35
|
+
# fixed. The checks are the package's TypeScript (`validateAgainstCatalog`
|
|
36
|
+
# and `layoutProblems`), so the loop is the caller's: run `design_layout`,
|
|
37
|
+
# check, and on a refusal run `repair_layout` with what the checks said, a
|
|
38
|
+
# bounded number of times.
|
|
39
|
+
#
|
|
32
40
|
# The split is the point. A single completion asked for a whole page decides
|
|
33
41
|
# the page and writes every patch in the same breath, and nothing in between
|
|
34
42
|
# can be read; the plan can. The seed reaches the planner only, because the
|
|
@@ -75,7 +83,9 @@ main_pipe = "design_layout"
|
|
|
75
83
|
# text, and fails loudly on any issue, so a repair is a change HERE, never to
|
|
76
84
|
# the fixture.
|
|
77
85
|
#
|
|
78
|
-
#
|
|
86
|
+
# Every model stage pins the same model: gpt-6.1-sol since the designer-reach
|
|
87
|
+
# spike, where a blind critic scored it level with claude-5.5-opus at about a
|
|
88
|
+
# third of the cost per page, with every layout valid. The specs pass takes MODEL=<id> to
|
|
79
89
|
# override every pin at once for a comparative run (and TEMPERATURE=<n> for a
|
|
80
90
|
# model that fixes its own), and records which model produced each fixture.
|
|
81
91
|
#
|
|
@@ -120,8 +130,18 @@ description = { type = "text", description = "One sentence on what the page is
|
|
|
120
130
|
name = { type = "text", description = "On an input page, the method's name as the host lists it - the one product name the page may carry. Absent when the host lists none." }
|
|
121
131
|
paths = { type = "list", item_type = "concept", item_concept_ref = "PathEntry", description = "Every path in the state the page binds or reads, in reading order and flattened: a structure's members follow it one level deeper, and a list's item members follow the list, spelled relative to one item.", required = true }
|
|
122
132
|
run_control = { type = "text", description = "On an input page, the catalog component that runs the method, by name; the page has exactly one of it." }
|
|
133
|
+
returns = { type = "list", item_type = "text", description = "On a composer, what one run gives back, one line per member of the result, read from the result's own titles and descriptions. Absent elsewhere." }
|
|
134
|
+
frame = { choices = ["page", "composer", "result"], description = "Where the page sits: `page`, a whole product page of its own; `composer`, the inputs of a host's compose-then-result app, before a run; `result`, the result page of that app, after one. Absent reads as `page`." }
|
|
135
|
+
style = { type = "concept", concept_ref = "Style", description = "The app style the team chose, which the page will wear: its name and its direction. Absent when the team chose none." }
|
|
123
136
|
sample_state = { type = "text", description = "On a result page, the state of one real run as the host loads it, as JSON - what `/result` held after one actual run, so magnitudes and lengths can be judged. Never copied into a page." }
|
|
124
137
|
|
|
138
|
+
[concept.Style]
|
|
139
|
+
description = "An app style: a named look the components draw - faces, weights, radii, panels and rhythm - and the one sentence that says what it is, which the composition follows."
|
|
140
|
+
|
|
141
|
+
[concept.Style.structure]
|
|
142
|
+
name = { type = "text", description = "The style's name, as a team picks it: `Quiet`, `Soft`, `Editorial`, `Bold`.", required = true }
|
|
143
|
+
direction = { type = "text", description = "The style's direction in one sentence, as a designer reads it.", required = true }
|
|
144
|
+
|
|
125
145
|
[concept.PathEntry]
|
|
126
146
|
description = "One path of the state, as the brief describes it: where it is, what it holds, what it is called, what constrains it, and whether the page lays it out or hands it back to the kernel's own control."
|
|
127
147
|
|
|
@@ -202,6 +222,42 @@ The result of a method, described below.
|
|
|
202
222
|
|
|
203
223
|
The method is called «{{ brief.name }}», as the host lists it. That is the app's name wherever the layout asks for one; invent no other. Links, if the layout carries any, name this page's own sections. The page states nothing the method does not: no promise about storage, privacy, speed, or what happens after the run.
|
|
204
224
|
|
|
225
|
+
{% endif %}
|
|
226
|
+
{% if brief.frame == "composer" %}
|
|
227
|
+
## Frame
|
|
228
|
+
|
|
229
|
+
This page is the composer of an app that is composed, then shows its result. The host draws what surrounds it: a band across the top with the team's logo and the app's name, which is not a heading, and below the page nothing. There is no side panel, no app bar, no footer and no summary of the inputs to build: the page is one centred column.
|
|
230
|
+
|
|
231
|
+
The column opens on a headline that says what the person gets from the app, in their words (the page's h1), and one line under it. Then the inputs, in one raised panel whose foot is the host's action row: how long a run takes, which the host knows, and the one control that runs the method, worded with the job. Inside the panel, group the inputs the way a person thinks about the request, under a few short titles, and set them in two columns when there are enough to fill two. Nothing restates an input, and nothing is said twice.
|
|
232
|
+
|
|
233
|
+
The action row stays on screen, so the first screen holds the headline and the first rows of the panel: put first the inputs a person decides first, and any the headline promises. Every word the page writes is fixed when the page is written, while the inputs change as a person fills them: never write a line about the state of an input or of the run, such as "Waiting on the topic" or "Add a document to start" - the controls show their own state.
|
|
234
|
+
{% if brief.returns %}
|
|
235
|
+
|
|
236
|
+
What one run gives back, read from the result's own description:
|
|
237
|
+
{% for line in brief.returns %}
|
|
238
|
+
- {{ line }}
|
|
239
|
+
{% endfor %}
|
|
240
|
+
|
|
241
|
+
A person runs a tool more readily when they know what comes back: the page may say it once, in their words, as the Composer's notes - a few short lines, never the list above copied out.
|
|
242
|
+
{% endif %}
|
|
243
|
+
|
|
244
|
+
{% elif brief.frame == "result" %}
|
|
245
|
+
## Frame
|
|
246
|
+
|
|
247
|
+
This page is the result page of an app that is composed, then shows its result: what the person sees after one run. The host draws what surrounds it: the band with the team's logo and the app's name, then a bar restating the request with a way back to edit it and to run again. The host also owns the actions on the result, such as downloading or sharing it, and places them where the page's header puts them. There is no side panel, no app bar and no footer to build, and nothing restates the request.
|
|
248
|
+
|
|
249
|
+
The page is the result across the full width. It opens on its header, which names the result in a person's words, read from the result itself where it can be (the page's h1). Then the figure or the fact a person looks for first, then the body of the result in the shape it naturally has: a plan day by day, options side by side to compare, a record's details. Lay out what a person reads; leave out what only a machine would.
|
|
250
|
+
|
|
251
|
+
Show each value once: a figure a component already states, such as the total inside a ProportionBar, is not repeated beside it. When the result carries the request back - the same facts the person entered, such as the topic or the tone they chose - leave that part out: the host's bar already restates the request. Write a value as a person reads it: a date in words, an amount beside its currency when the result has one.
|
|
252
|
+
|
|
253
|
+
{% endif %}
|
|
254
|
+
{% if brief.style %}
|
|
255
|
+
## Style
|
|
256
|
+
|
|
257
|
+
The team's apps wear the {{ brief.style.name }} style: {{ brief.style.direction }}
|
|
258
|
+
|
|
259
|
+
The components already draw its faces, weights, radii and panels, so the page sets none of them. What the page decides is its composition, and the composition should read as this style too: how much air there is between the groups, how large and how long the headline is, how many groups the inputs fall into and whether they are titled, how much the page says around them. A calm style asks for few groups and generous air, a literary one for a headline that reads like a title and a measured rhythm, a graphic one for a dense, firm page with short labels.
|
|
260
|
+
|
|
205
261
|
{% endif %}
|
|
206
262
|
## State
|
|
207
263
|
|
|
@@ -252,7 +308,11 @@ None. Emit no `/state` patches.
|
|
|
252
308
|
## Run
|
|
253
309
|
|
|
254
310
|
{% if gating %}
|
|
311
|
+
{% if brief.frame == "composer" %}
|
|
312
|
+
The run waits for `{{ gating | map(attribute="name") | join("`, `") }}`. The host's action row says so by itself, so the page writes nothing about it.
|
|
313
|
+
{% else %}
|
|
255
314
|
The run waits for `{{ gating | map(attribute="name") | join("`, `") }}`; say so near the {{ brief.run_control }}, briefly.
|
|
315
|
+
{% endif %}
|
|
256
316
|
{% else %}
|
|
257
317
|
Nothing gates the run.
|
|
258
318
|
{% endif %}
|
|
@@ -274,7 +334,7 @@ type = "PipeLLM"
|
|
|
274
334
|
description = "Read the brief and plan the page: what it is for, how it is composed, what each region holds and with which components, what runs the method, and what is delegated."
|
|
275
335
|
inputs = { catalog = "Catalog", brief_text = "Text", seed = "Text?" }
|
|
276
336
|
output = "PagePlan"
|
|
277
|
-
model = { model = "
|
|
337
|
+
model = { model = "gpt-6.1-sol", temperature = 0.5, max_tokens = 8192 }
|
|
278
338
|
system_prompt = """
|
|
279
339
|
You are a product designer with taste. You plan ONE page for a method - its inputs, or its result - to be built from a fixed catalog of components and bound to state the host provides. The page must read as a small, purpose-built web app, never as a form. Its shape is yours to decide: nothing here prescribes one. You write the plan and a second designer builds the page from it, so every decision you leave open is one they will make without you.
|
|
280
340
|
"""
|
|
@@ -293,6 +353,8 @@ DESIGN DIRECTION:
|
|
|
293
353
|
- Restraint reads premium. No label that restates the obvious, no card inside a card, no paragraph that explains the form, no more than two levels of heading. Every element must earn its place; when in doubt, remove it.
|
|
294
354
|
- Write copy a person would say: short labels, one helper sentence where a label is not enough and none where it is. The control that runs names the job ("Plan my trip"), never "Submit".
|
|
295
355
|
- Every value binds to a path the brief lists, and the page reads clearly in both a light and a dark theme.
|
|
356
|
+
- When the brief has a Frame section, the host has already drawn the rest of the screen: plan only the page that section describes, inside that frame - never a side panel, never a bar or a footer the host draws, never a second place the method is run from or a second copy of the request.
|
|
357
|
+
- When the brief has a Style section, the team's apps wear that style, and the composition follows its direction: how much air the page takes, how large the headline reads, how dense the inputs sit and how they are grouped are yours to set in its spirit. The look itself - faces, radii, panels, colour - is the components', never the page's.
|
|
296
358
|
|
|
297
359
|
AVAILABLE COMPONENTS ({{ catalog.components | length }}):
|
|
298
360
|
|
|
@@ -337,12 +399,13 @@ type = "PipeLLM"
|
|
|
337
399
|
description = "Build the page the plan describes: emit the JSONL patches of a spec over the catalog, bound to the host's state at the paths the brief lists."
|
|
338
400
|
inputs = { plan = "PagePlan", catalog = "Catalog", brief_text = "Text" }
|
|
339
401
|
output = "Text"
|
|
340
|
-
# The object form, because the default output cap (4096 tokens on
|
|
341
|
-
# is not enough for a page over a
|
|
342
|
-
#
|
|
402
|
+
# The object form, because the default output cap (4096 tokens on
|
|
403
|
+
# claude-5-sonnet, where this was learned) is not enough for a page over a
|
|
404
|
+
# deeply nested result: the Company hero's first run spent exactly 4096
|
|
405
|
+
# tokens and came back empty. The object form
|
|
343
406
|
# requires a temperature; this is the runtime's own default, so the setting
|
|
344
407
|
# the earlier captures ran under is unchanged.
|
|
345
|
-
model = { model = "
|
|
408
|
+
model = { model = "gpt-6.1-sol", temperature = 0.5, max_tokens = 16384 }
|
|
346
409
|
system_prompt = """
|
|
347
410
|
You are a product designer with taste, building ONE page for a method - its inputs, or its result - as a JSON spec over a fixed catalog of components, bound to state the host provides. A designer has planned the page; you build exactly that page, and it must read as a small, purpose-built web app, never as a form.
|
|
348
411
|
"""
|
|
@@ -402,6 +465,7 @@ EXECUTION:
|
|
|
402
465
|
- Restraint reads premium. No label that restates the obvious, no card inside a card, no paragraph that explains the form, no more than two levels of heading. Every element must earn its place; when in doubt, remove it.
|
|
403
466
|
- Write copy a person would say: short labels, one helper sentence where a label is not enough and none where it is. The control that runs names the job ("Plan my trip"), never "Submit".
|
|
404
467
|
- Every value binds to a path the brief lists, and the page reads clearly in both a light and a dark theme.
|
|
468
|
+
- When the brief has a Style section, build the plan's composition in its spirit: where the plan is silent - a gap, a column count, whether a group is titled - decide as that style would.
|
|
405
469
|
|
|
406
470
|
AVAILABLE COMPONENTS ({{ catalog.components | length }}):
|
|
407
471
|
|
|
@@ -450,3 +514,66 @@ Below is the brief for the page you are building. It is the ONLY source of paths
|
|
|
450
514
|
|
|
451
515
|
Build this page as the plan describes. Bind every value; inline nothing the state carries. Output only the JSONL patch lines - no prose before or after, no code fence.
|
|
452
516
|
"""
|
|
517
|
+
|
|
518
|
+
[pipe.repair_layout]
|
|
519
|
+
type = "PipeSequence"
|
|
520
|
+
description = "Repair a page the host's checks refused: lay the brief out, then emit the whole page again with every problem the checks named fixed."
|
|
521
|
+
inputs = { catalog = "Catalog", brief = "Brief", page = "Text", problems = "Text" }
|
|
522
|
+
output = "Text"
|
|
523
|
+
steps = [
|
|
524
|
+
{ pipe = "render_brief", result = "brief_text" },
|
|
525
|
+
{ pipe = "emit_repair", result = "repaired" },
|
|
526
|
+
]
|
|
527
|
+
|
|
528
|
+
[pipe.emit_repair]
|
|
529
|
+
type = "PipeLLM"
|
|
530
|
+
description = "Emit a refused page again, whole, as JSONL patches over the catalog, with every problem the host's checks named fixed and nothing else changed."
|
|
531
|
+
inputs = { page = "Text", problems = "Text", catalog = "Catalog", brief_text = "Text" }
|
|
532
|
+
output = "Text"
|
|
533
|
+
model = { model = "gpt-6.1-sol", temperature = 0.5, max_tokens = 16384 }
|
|
534
|
+
system_prompt = """
|
|
535
|
+
You are a product designer with taste, repairing ONE page that was built as a JSON spec over a fixed catalog of components, bound to state the host provides. The host checked the page before rendering it and refused it; you fix what it refused and keep everything else exactly as it was designed.
|
|
536
|
+
"""
|
|
537
|
+
prompt = """
|
|
538
|
+
OUTPUT FORMAT (JSONL, RFC 6902 JSON Patch):
|
|
539
|
+
Output ONLY JSONL: one JSON object per line, no prose, no markdown, no code fences. Emit the WHOLE page again, every line of it, as the page below is written: first /root, then each element under /elements/<key>, parents before children, then the /state patches it had, if any.
|
|
540
|
+
|
|
541
|
+
WHAT THE HOST'S CHECKS FOUND:
|
|
542
|
+
Each line names an element by its key and the rule it breaks.
|
|
543
|
+
|
|
544
|
+
@problems
|
|
545
|
+
|
|
546
|
+
THE PAGE AS IT WAS EMITTED:
|
|
547
|
+
|
|
548
|
+
@page
|
|
549
|
+
|
|
550
|
+
HOW TO REPAIR IT:
|
|
551
|
+
1. Fix every problem listed, at the element it names, in the smallest way that satisfies the rule: a prop the component does not declare is removed or replaced by one it declares; a missing required prop is added; a second h1 becomes the next level down; a heading that skips a level takes the level it should have; a Tabs or Steps with one panel becomes that panel alone; a path the brief does not list is replaced by the one the brief lists for that value, or the element is removed.
|
|
552
|
+
2. Change nothing the problems do not name: every other element keeps its key, its type, its props, its copy and its place in the tree, and the page keeps its composition.
|
|
553
|
+
3. A fix must not break another rule: one h1, heading levels that increase by one, only the components and props below, every child emitted as its own element, every path one the brief lists, one control that runs the method on an input page.
|
|
554
|
+
|
|
555
|
+
AVAILABLE COMPONENTS ({{ catalog.components | length }}):
|
|
556
|
+
|
|
557
|
+
{% for component in catalog.components -%}
|
|
558
|
+
- {{ component.name }}: {{ component.props }}
|
|
559
|
+
{%- if component.description %} - {{ component.description }}{% endif %}
|
|
560
|
+
{%- if component.accepts_children and component.slots %} [accepts children; slots: {{ component.slots | join(", ") }}]
|
|
561
|
+
{%- elif component.accepts_children %} [accepts children]
|
|
562
|
+
{%- elif component.slots %} [slots: {{ component.slots | join(", ") }}]{% endif %}
|
|
563
|
+
{%- if component.events %} [events: {{ component.events | join(", ") }}]{% endif %}
|
|
564
|
+
{% endfor %}
|
|
565
|
+
AVAILABLE ACTIONS:
|
|
566
|
+
|
|
567
|
+
{% for action in catalog.actions -%}
|
|
568
|
+
- {{ action.name }}: {{ action.description }}{% if action.built_in %} [built-in]{% endif %}
|
|
569
|
+
{% endfor %}
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
Below is the brief the page was built for. It is the ONLY source of paths: every value the page shows or binds must be at a path the brief lists, spelled exactly as written.
|
|
573
|
+
|
|
574
|
+
@brief_text
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
Emit the repaired page, whole. Output only the JSONL patch lines - no prose before or after, no code fence.
|
|
579
|
+
"""
|
package/dist/react/index.d.ts
CHANGED
|
@@ -124,9 +124,13 @@ interface ObjectFieldProps {
|
|
|
124
124
|
env?: FieldEnv;
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
|
-
* A structured concept: its sub-fields
|
|
128
|
-
*
|
|
129
|
-
*
|
|
127
|
+
* A structured concept: its sub-fields under its name, grouped by where the
|
|
128
|
+
* structure stands (`StructureGrouping`): a card for a form's own structure,
|
|
129
|
+
* or a section when the host has framed the form in its own panel, a rule
|
|
130
|
+
* for one inside another, and nothing for a list's row, so nesting
|
|
131
|
+
* stays legible without boxing a field into a sliver of a phone. Empty
|
|
132
|
+
* optional sub-fields collapse behind a "+ N optional" toggle - required and
|
|
133
|
+
* already-filled fields always show.
|
|
130
134
|
*
|
|
131
135
|
* A CLOSED optional sub-structure (absent value) folds behind that toggle and
|
|
132
136
|
* is counted like any empty optional field. The toggle also opens and closes
|
|
@@ -859,6 +863,18 @@ interface MethodFormProps {
|
|
|
859
863
|
strings?: Partial<FieldStrings>;
|
|
860
864
|
/** Mounts `FieldPresentationProvider`; absent, the nearest one applies. */
|
|
861
865
|
presentation?: FieldPresentation;
|
|
866
|
+
/**
|
|
867
|
+
* The host draws the form inside a panel of its own, so the form draws no
|
|
868
|
+
* card inside it: a structure among the inputs is a section, its name a
|
|
869
|
+
* heading over its fields, set off from the inputs beside it by a hairline
|
|
870
|
+
* rather than boxed. A section the run gate refuses draws a red bar down
|
|
871
|
+
* its side, 4 pixels off the form, and sets its message beside its
|
|
872
|
+
* heading, where a card would turn its outline red, so the panel leaves
|
|
873
|
+
* the form 7 pixels of unclipped room at its start edge. Absent, a
|
|
874
|
+
* structure that is an input draws its own card, which is what says where
|
|
875
|
+
* it ends on a bare page.
|
|
876
|
+
*/
|
|
877
|
+
framed?: boolean;
|
|
862
878
|
/** Classes for the stack the fields sit in. */
|
|
863
879
|
className?: string;
|
|
864
880
|
}
|
|
@@ -872,7 +888,7 @@ interface MethodFormProps {
|
|
|
872
888
|
* disclosure folds and opens exactly as the one inside a structure does,
|
|
873
889
|
* optional structures included.
|
|
874
890
|
*/
|
|
875
|
-
declare function MethodForm({ locale, strings, presentation, ...props }: MethodFormProps): react.ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
891
|
+
declare function MethodForm({ locale, strings, presentation, framed, ...props }: MethodFormProps): react.ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
876
892
|
|
|
877
893
|
interface ResultFieldProps {
|
|
878
894
|
field: RunField;
|
|
@@ -1344,7 +1360,9 @@ interface StuffViewerProps {
|
|
|
1344
1360
|
* second heading under its own.
|
|
1345
1361
|
*
|
|
1346
1362
|
* It also supplies `downloadBaseName`'s default, so the file a reader saves
|
|
1347
|
-
* is named after the thing they were reading rather than `output.json`.
|
|
1363
|
+
* is named after the thing they were reading rather than `output.json`. It
|
|
1364
|
+
* replaces a `title` the root carries as well: that names the output slot,
|
|
1365
|
+
* not the stuff.
|
|
1348
1366
|
*/
|
|
1349
1367
|
name?: string;
|
|
1350
1368
|
/**
|
|
@@ -1372,7 +1390,7 @@ interface StuffViewerProps {
|
|
|
1372
1390
|
downloadBaseName?: string;
|
|
1373
1391
|
/**
|
|
1374
1392
|
* Which download controls this panel draws, laid key by key over what
|
|
1375
|
-
* `ResultEnvProvider` says: `{ result: false }` drops the
|
|
1393
|
+
* `ResultEnvProvider` says: `{ result: false }` drops the panel's Download
|
|
1376
1394
|
* and keeps each file's own button, `{ files: false }` the reverse. For a
|
|
1377
1395
|
* surface where saving makes no sense (a preview inside an editor, say), set
|
|
1378
1396
|
* both to `false`.
|
package/dist/react/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, SUPPORTED_LOCALES, StuffViewer, TextField, UnknownField, fieldControlClass, fieldStringsFor, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl } from '../chunk-
|
|
2
|
+
export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, SUPPORTED_LOCALES, StuffViewer, TextField, UnknownField, fieldControlClass, fieldStringsFor, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl } from '../chunk-ENK5QTUK.js';
|
|
3
|
+
import '../chunk-2UCH4AAO.js';
|
|
3
4
|
import '../chunk-4VAO6BDE.js';
|
|
4
5
|
import '../chunk-VH5CDQDR.js';
|
|
5
6
|
import '../chunk-L23R3JNQ.js';
|
|
6
7
|
import '../chunk-56WECFBD.js';
|
|
7
|
-
import '../chunk-ZX52CQJ4.js';
|
|
8
|
-
export { humanizeFieldName } from '../chunk-GHVVULHY.js';
|
|
9
|
-
import '../chunk-QQK6Y55E.js';
|
|
10
|
-
import '../chunk-ZB4RYUKV.js';
|
|
11
|
-
import '../chunk-FABY3DEY.js';
|
|
12
|
-
import '../chunk-XCO6GTEC.js';
|
|
13
8
|
import '../chunk-E6BIXJ6C.js';
|
|
14
9
|
import '../chunk-XO5OENNF.js';
|
|
10
|
+
export { humanizeFieldName } from '../chunk-GHVVULHY.js';
|
|
11
|
+
import '../chunk-QQK6Y55E.js';
|
|
12
|
+
import '../chunk-HVWE3TTC.js';
|
|
15
13
|
import '../chunk-OD3VYXUY.js';
|
|
16
14
|
import '../chunk-WYH56SOZ.js';
|
|
17
15
|
import '../chunk-V3OV2XQT.js';
|
|
16
|
+
import '../chunk-FABY3DEY.js';
|
|
17
|
+
import '../chunk-XCO6GTEC.js';
|
|
18
18
|
import '../chunk-GKV77RAS.js';
|
|
19
19
|
//# sourceMappingURL=index.js.map
|
|
20
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The brand contract, as data: which token of a brand's `tokens.json` sets
|
|
5
|
+
* which custom property, and what that property paints.
|
|
6
|
+
*
|
|
7
|
+
* It is the theme contract seen from the other side. `src/styles/theme.css`
|
|
8
|
+
* states it as a working default and `docs/theming.md` in prose; this is the
|
|
9
|
+
* same list as a table a build can read, and a brand is exactly these tokens
|
|
10
|
+
* and no others - the validator refuses a file that adds one, because a token
|
|
11
|
+
* nothing consumes is a value with no way to be looked at.
|
|
12
|
+
*
|
|
13
|
+
* The colour and radius rows are not a second list kept beside `theme.css`:
|
|
14
|
+
* they are held to it, by `__tests__/contract.test.ts` over the source and by
|
|
15
|
+
* `scripts/assert-bundle.mjs` over the build. The two typography rows are the
|
|
16
|
+
* exception, and on purpose. `--font-sans` and `--font-mono` are the names of
|
|
17
|
+
* Tailwind's own theme variables, which the prebuilt sheet already declares,
|
|
18
|
+
* so `theme.css` does not define them: it is unlayered, and a value there
|
|
19
|
+
* would override the typeface of every host that loads it. The stock
|
|
20
|
+
* typeface is therefore the host's own, and only a brand's scope sets these
|
|
21
|
+
* two. See docs/brand.md.
|
|
22
|
+
*/
|
|
23
|
+
type ContractTokenType = 'color' | 'dimension' | 'fontFamily';
|
|
24
|
+
interface ContractToken {
|
|
25
|
+
/** The DTCG token id, `color.background`. */
|
|
26
|
+
id: string;
|
|
27
|
+
/** The custom property it sets, `--background`. */
|
|
28
|
+
variable: string;
|
|
29
|
+
type: ContractTokenType;
|
|
30
|
+
/** What the property paints, in the words a producer is told. */
|
|
31
|
+
paints: string;
|
|
32
|
+
}
|
|
33
|
+
/** The colour tokens, in the order `theme.css` states them. */
|
|
34
|
+
declare const COLOR_TOKEN_NAMES: readonly ["background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary", "primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent", "accent-foreground", "destructive", "destructive-foreground", "border", "input", "ring"];
|
|
35
|
+
type ColorTokenName = (typeof COLOR_TOKEN_NAMES)[number];
|
|
36
|
+
declare function isColorTokenName(name: string): name is ColorTokenName;
|
|
37
|
+
declare const BRAND_CONTRACT: readonly ContractToken[];
|
|
38
|
+
/** The minimum contrast ratio text must reach: WCAG 2 AA for normal text. */
|
|
39
|
+
declare const MIN_CONTRAST = 4.5;
|
|
40
|
+
/**
|
|
41
|
+
* The contrast a control's outline or mark needs against what surrounds it:
|
|
42
|
+
* WCAG 2's level for a user interface component (1.4.11). A button colour
|
|
43
|
+
* below it against the page is a button whose shape is hard to see.
|
|
44
|
+
*/
|
|
45
|
+
declare const MIN_CONTROL_CONTRAST = 3;
|
|
46
|
+
/**
|
|
47
|
+
* Two colours the contract holds to a contrast ratio in both modes, as they
|
|
48
|
+
* render: the canvas opaque, a translucent surface laid over it, and the ink
|
|
49
|
+
* blended over whatever is beneath it by its alpha.
|
|
50
|
+
*/
|
|
51
|
+
interface ContrastPair {
|
|
52
|
+
foreground: ColorTokenName;
|
|
53
|
+
background: ColorTokenName;
|
|
54
|
+
/**
|
|
55
|
+
* The canvas beneath the pair's surface. When it is set, `background` names
|
|
56
|
+
* a surface the contract lets be translucent, and that surface is laid over
|
|
57
|
+
* this canvas before anything is measured on it. Absent, `background` is
|
|
58
|
+
* the canvas itself and must be opaque, since what shows through a
|
|
59
|
+
* translucent canvas is whatever the host paints beneath it.
|
|
60
|
+
*/
|
|
61
|
+
over?: ColorTokenName;
|
|
62
|
+
/** The ratio the pair must reach: `MIN_CONTRAST` for text, `MIN_CONTROL_CONTRAST` for a mark. */
|
|
63
|
+
minimum: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The pairs a brand must hold in both modes. The ink and the secondary text
|
|
67
|
+
* on the canvas are text, at WCAG AA.
|
|
68
|
+
*
|
|
69
|
+
* The secondary text on the field fill is held to a control's 3:1. The kernel
|
|
70
|
+
* draws marks in that colour on the fill, an off switch's track and a number
|
|
71
|
+
* field's stepper icons, and those need 3:1 to be seen. It also sets text in
|
|
72
|
+
* that colour on the fill, a boolean row's description, an unselected pill's
|
|
73
|
+
* label and a placeholder, which AA would hold to 4.5:1, but the stock
|
|
74
|
+
* palette's light mode measures 4.39:1 on its own field fill, the same grey
|
|
75
|
+
* it measures on `--muted`, so the text bar there waits on the stock palette
|
|
76
|
+
* reaching it. The field fill may be translucent, as a style's often is, so
|
|
77
|
+
* it is laid over the canvas first.
|
|
78
|
+
*
|
|
79
|
+
* The error colour is held to the same 3:1, as a solid colour, on the canvas
|
|
80
|
+
* and on a card laid over it, which is what a host's panel usually is,
|
|
81
|
+
* because a framed section's error bar is drawn in that colour, solid, on
|
|
82
|
+
* either. The outlines of a card and of a field in error are drawn at half
|
|
83
|
+
* and seven tenths of it, so they read fainter than this pair measures, and
|
|
84
|
+
* the pair does not hold them. The error captions are text, which AA would
|
|
85
|
+
* hold to 4.5:1, but the stock palette's red measures 3.76:1 on white, so
|
|
86
|
+
* that bar waits on the stock palette too.
|
|
87
|
+
*/
|
|
88
|
+
declare const CONTRAST_PAIRS: readonly ContrastPair[];
|
|
89
|
+
/** The custom property a token sets, or a loud failure for a token the contract does not name. */
|
|
90
|
+
declare function contractVariable(id: string): string;
|
|
91
|
+
|
|
92
|
+
declare const brandManifestSchema: z.ZodObject<{
|
|
93
|
+
name: z.ZodString;
|
|
94
|
+
website: z.ZodURL;
|
|
95
|
+
logo: z.ZodObject<{
|
|
96
|
+
onLight: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
|
|
97
|
+
onDark: z.ZodUnion<readonly [z.ZodURL, z.ZodString]>;
|
|
98
|
+
}, z.core.$strict>;
|
|
99
|
+
webfont: z.ZodNullable<z.ZodObject<{
|
|
100
|
+
provider: z.ZodLiteral<"google-fonts">;
|
|
101
|
+
family: z.ZodString;
|
|
102
|
+
}, z.core.$strict>>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
type BrandManifest = z.infer<typeof brandManifestSchema>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The app styles and the typefaces, as data, and the roles a team's choice of
|
|
108
|
+
* the two sets.
|
|
109
|
+
*
|
|
110
|
+
* A style is a small set of design values a team picks from a closed list, on
|
|
111
|
+
* top of its own colours and logo: how its headings, its panels, its corners,
|
|
112
|
+
* its call to action and its rhythm look. A typeface is picked from a short
|
|
113
|
+
* list beside it, and replaces the style's body face, and its heading face
|
|
114
|
+
* wherever the style's headings follow the body. Neither adds a colour: every
|
|
115
|
+
* fill, edge and ink a style uses is a role the theme contract already has,
|
|
116
|
+
* named here by that role (`RoleColor`), or a shadow.
|
|
117
|
+
*
|
|
118
|
+
* `deriveStyleRoles` returns roles, never custom property names, the way
|
|
119
|
+
* `deriveBrandRoles` does for the seed colours: each host maps them onto its
|
|
120
|
+
* own tokens, and the kernel's controls and the generative layer read the few
|
|
121
|
+
* optional tokens docs/theming.md lists, each with today's look as its
|
|
122
|
+
* fallback. A role a choice does not set is not a key, so a host keeps its own
|
|
123
|
+
* value there; `quiet` with no typeface sets nothing at all, which is what
|
|
124
|
+
* makes "no style" and "the default style" one state.
|
|
125
|
+
*
|
|
126
|
+
* See docs/brand.md § "App styles and typefaces".
|
|
127
|
+
*/
|
|
128
|
+
/** The faces of the closed list, all on Google Fonts under the SIL Open Font License. */
|
|
129
|
+
type TypefaceId = 'inter' | 'dm-sans' | 'plus-jakarta-sans' | 'manrope' | 'ibm-plex-sans' | 'source-sans-3' | 'work-sans' | 'space-grotesk' | 'newsreader';
|
|
130
|
+
/** The faces a team may pick as its typeface: every face but a style's own heading face. */
|
|
131
|
+
type BodyTypefaceId = Exclude<TypefaceId, 'newsreader'>;
|
|
132
|
+
interface Typeface {
|
|
133
|
+
id: TypefaceId;
|
|
134
|
+
/** The family name, as a stylesheet and a font service name it. */
|
|
135
|
+
family: string;
|
|
136
|
+
category: 'sans-serif' | 'serif';
|
|
137
|
+
/** The weights a host loads it in: from the host's own light titles (300) to the boldest a style sets (700). */
|
|
138
|
+
weights: readonly number[];
|
|
139
|
+
/** The generic stack that renders while the face loads, or when it never does. */
|
|
140
|
+
fallback: string;
|
|
141
|
+
/** The face first, then its fallback: a whole `font-family` value. */
|
|
142
|
+
stack: string;
|
|
143
|
+
/** Whether a team may pick it (`body`), or a style uses it for its headings only (`heading`). */
|
|
144
|
+
use: 'body' | 'heading';
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Every face a style or a team can set. The body faces are the list a team
|
|
148
|
+
* picks from; Newsreader is the `editorial` style's heading face, listed so a
|
|
149
|
+
* host loads it, and is not a team's choice. Frozen, like `APP_STYLES`: a
|
|
150
|
+
* process serves many teams, so the catalogue one of them reads cannot be
|
|
151
|
+
* one another has edited.
|
|
152
|
+
*/
|
|
153
|
+
declare const TYPEFACES: readonly Typeface[];
|
|
154
|
+
/** Whether `value` names a face a team may pick: a body face of `TYPEFACES`. */
|
|
155
|
+
declare function isTypeface(value: unknown): value is BodyTypefaceId;
|
|
156
|
+
/**
|
|
157
|
+
* A colour a style paints with, named by the role of the theme contract it
|
|
158
|
+
* is: never a colour of its own, so a team's palette and the host's theme
|
|
159
|
+
* stay the only sources of colour, and their readability guarantees hold.
|
|
160
|
+
*/
|
|
161
|
+
interface RoleColor {
|
|
162
|
+
role: ColorTokenName;
|
|
163
|
+
/** Mixed toward another role, in Oklab, by `share` (0 to 1): the card a little toward the foreground. */
|
|
164
|
+
toward?: {
|
|
165
|
+
role: ColorTokenName;
|
|
166
|
+
share: number;
|
|
167
|
+
};
|
|
168
|
+
/** Its opacity over what lies behind it, 0 to 1; absent, opaque. */
|
|
169
|
+
alpha?: number;
|
|
170
|
+
}
|
|
171
|
+
/** How a style draws a panel - a host's composer, a designed page's rail or card - in one theme. */
|
|
172
|
+
interface PanelRoles {
|
|
173
|
+
fill: RoleColor;
|
|
174
|
+
/** The edge's colour, or `null` for no visible edge (the width is kept, so a panel's box does not move). */
|
|
175
|
+
edge: RoleColor | null;
|
|
176
|
+
/** The edge's width, in CSS pixels. */
|
|
177
|
+
edgeWidth: number;
|
|
178
|
+
/** A CSS `box-shadow` in a neutral ink, or `null` for none. */
|
|
179
|
+
shadow: string | null;
|
|
180
|
+
/**
|
|
181
|
+
* The fill of the fields set into the panel, where the host's field fill
|
|
182
|
+
* would sink into it; absent, the host's own.
|
|
183
|
+
*/
|
|
184
|
+
field?: RoleColor;
|
|
185
|
+
}
|
|
186
|
+
/** The call to action's shadow: a CSS shadow's geometry, in pixels, tinted with a role. */
|
|
187
|
+
interface ActionShadow {
|
|
188
|
+
x: number;
|
|
189
|
+
y: number;
|
|
190
|
+
blur: number;
|
|
191
|
+
spread: number;
|
|
192
|
+
color: RoleColor;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The roles a style and a typeface set. An absent role is not a key: a host
|
|
196
|
+
* keeps its own value there.
|
|
197
|
+
*/
|
|
198
|
+
interface StyleRoles {
|
|
199
|
+
/** The face of everything on the page. */
|
|
200
|
+
bodyFace?: Typeface;
|
|
201
|
+
/** The face of the page's titles and its sections' and panels' headings. */
|
|
202
|
+
headingFace?: Typeface;
|
|
203
|
+
headingWeight?: number;
|
|
204
|
+
/** The headings' letter spacing, in `em`. */
|
|
205
|
+
headingTracking?: string;
|
|
206
|
+
/** A control's corner radius, in CSS pixels; a panel takes one and a half of it. */
|
|
207
|
+
radius?: number;
|
|
208
|
+
/** The panel treatment in each theme: a shadow that reads on a light page is lost on a dark one. */
|
|
209
|
+
panel?: {
|
|
210
|
+
light: PanelRoles;
|
|
211
|
+
dark: PanelRoles;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* The call to action's shadow in each theme, or `none` for a plain fill: a
|
|
215
|
+
* shadow in the button colour that lifts a button on a light page smears
|
|
216
|
+
* under it on a dark one.
|
|
217
|
+
*/
|
|
218
|
+
actionShadow?: {
|
|
219
|
+
light: ActionShadow | 'none';
|
|
220
|
+
dark: ActionShadow | 'none';
|
|
221
|
+
};
|
|
222
|
+
/** The rhythm: a multiple of the host's own margins, paddings and gaps around the form. Never a control's size. */
|
|
223
|
+
density?: number;
|
|
224
|
+
}
|
|
225
|
+
type AppStyleId = 'quiet' | 'soft' | 'editorial' | 'bold';
|
|
226
|
+
interface AppStyle {
|
|
227
|
+
id: AppStyleId;
|
|
228
|
+
name: string;
|
|
229
|
+
/** The direction, in one sentence, as a designer reads it. */
|
|
230
|
+
direction: string;
|
|
231
|
+
/** The roles the style sets when the team picks no typeface. */
|
|
232
|
+
roles: StyleRoles;
|
|
233
|
+
}
|
|
234
|
+
declare const APP_STYLE_IDS: readonly AppStyleId[];
|
|
235
|
+
/** Whether `value` names one of the app styles. */
|
|
236
|
+
declare function isAppStyle(value: unknown): value is AppStyleId;
|
|
237
|
+
/**
|
|
238
|
+
* The roles a team's choice sets: its style and its typeface, each a stored
|
|
239
|
+
* id or nothing. An unknown or absent style is `quiet`, which sets nothing;
|
|
240
|
+
* an unknown typeface, or one a team may not pick, is no typeface, so the
|
|
241
|
+
* style's own face (or the host's) stays. The team's typeface replaces the
|
|
242
|
+
* style's body face, and its heading face wherever the style's headings
|
|
243
|
+
* follow the body.
|
|
244
|
+
*/
|
|
245
|
+
declare function deriveStyleRoles(style: string | null | undefined, typeface?: string | null): StyleRoles;
|
|
246
|
+
/** The styles, each with its own roles: what it sets for a team that picked no typeface. */
|
|
247
|
+
declare const APP_STYLES: Readonly<Record<AppStyleId, AppStyle>>;
|
|
248
|
+
/**
|
|
249
|
+
* A role colour as a `#rrggbb` hex over the palette it names, to measure a
|
|
250
|
+
* surface a style draws: the ink a team's accent needs on a style's panel
|
|
251
|
+
* (`deriveBrandRoles`' `surfaces`). The mix is Oklab's, as CSS `color-mix()`
|
|
252
|
+
* computes it. A translucent colour is laid over `behind`; without it, it
|
|
253
|
+
* throws, since a translucent colour has no one value. Throws, too, for a
|
|
254
|
+
* role the palette does not hold or a palette colour that is not a hex.
|
|
255
|
+
*/
|
|
256
|
+
declare function resolveRoleColor(color: RoleColor, palette: Partial<Record<ColorTokenName, string>>, behind?: string): string;
|
|
257
|
+
|
|
258
|
+
export { APP_STYLES as A, type BrandManifest as B, type ColorTokenName as C, MIN_CONTRAST as M, type PanelRoles as P, type RoleColor as R, type StyleRoles as S, TYPEFACES as T, APP_STYLE_IDS as a, type ActionShadow as b, type AppStyle as c, type AppStyleId as d, BRAND_CONTRACT as e, type BodyTypefaceId as f, COLOR_TOKEN_NAMES as g, CONTRAST_PAIRS as h, type ContractToken as i, type ContractTokenType as j, type ContrastPair as k, MIN_CONTROL_CONTRAST as l, type Typeface as m, type TypefaceId as n, brandManifestSchema as o, contractVariable as p, deriveStyleRoles as q, isAppStyle as r, isColorTokenName as s, isTypeface as t, resolveRoleColor as u };
|