@pipelex/mthds-form 0.20.0 → 0.22.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/README.md +1 -1
- package/dist/brand/index.d.ts +109 -1
- package/dist/brand/index.js +113 -1
- package/dist/brand/index.js.map +1 -1
- package/dist/{chunk-CSLDIWB6.js → chunk-5IZWP2X4.js} +339 -69
- package/dist/chunk-5IZWP2X4.js.map +1 -0
- package/dist/field-renderer-lNhtw-Cz.d.ts +215 -0
- package/dist/generative/index.d.ts +1 -1
- package/dist/generative/index.js +21 -5
- package/dist/generative/index.js.map +1 -1
- package/dist/react/index.d.ts +86 -71
- package/dist/react/index.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/dist/chunk-CSLDIWB6.js.map +0 -1
- package/dist/field-renderer-B3g16qZT.d.ts +0 -74
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { FileRunField, RunField } from './core/descriptor.js';
|
|
3
|
+
import { FileSource } from './core/sources.js';
|
|
4
|
+
|
|
5
|
+
/** The serializable value of a file field. URL is what the runner sends. */
|
|
6
|
+
interface FileValue {
|
|
7
|
+
url?: string;
|
|
8
|
+
filename?: string;
|
|
9
|
+
}
|
|
10
|
+
/** What a host's upload resolves to: the stored file's URL, or its URL and name. */
|
|
11
|
+
type UploadedFile = string | {
|
|
12
|
+
url: string;
|
|
13
|
+
filename?: string;
|
|
14
|
+
};
|
|
15
|
+
interface FileFieldProps {
|
|
16
|
+
field: FileRunField;
|
|
17
|
+
value: FileValue | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Called when a file is dropped/picked - the parent uploads and sets value.
|
|
20
|
+
*
|
|
21
|
+
* It may return the upload: a promise of what it stored, whose URL is the
|
|
22
|
+
* one the host writes into the value. The control then knows its upload's
|
|
23
|
+
* own result by that URL, so the dropped file's preview is kept for it
|
|
24
|
+
* whichever render the host writes it in, and a rejection retires the
|
|
25
|
+
* preview at once. Returning nothing leaves the control to judge the upload
|
|
26
|
+
* by when its value lands (docs/upload-seam.md).
|
|
27
|
+
*
|
|
28
|
+
* Absent means the host cannot store a file, and the control then offers no
|
|
29
|
+
* dropzone and no picker: the link input is open from the start, with a line
|
|
30
|
+
* saying a file cannot be uploaded here.
|
|
31
|
+
*/
|
|
32
|
+
onDropFile?: (file: File) => void | PromiseLike<UploadedFile | void>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether the link input is offered, `true` when absent. `false` removes the
|
|
35
|
+
* "paste a URL instead" toggle and the input behind it, and says nothing about
|
|
36
|
+
* which values are valid. With no `onDropFile` either, the control throws.
|
|
37
|
+
*/
|
|
38
|
+
allowUrl?: boolean;
|
|
39
|
+
/** Manual URL paste / clear flows through here too. */
|
|
40
|
+
onChange: (value: FileValue | undefined) => void;
|
|
41
|
+
id: string;
|
|
42
|
+
/** Parent-driven async state. */
|
|
43
|
+
uploading?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Why the host's upload of this field's file failed. Shown in the alert slot
|
|
46
|
+
* a refused format uses, until the host removes it. A clear or a typed link
|
|
47
|
+
* hides it until the host sends a different message or removes it; a refused
|
|
48
|
+
* pick shows its refusal in its place.
|
|
49
|
+
*/
|
|
50
|
+
uploadError?: string;
|
|
51
|
+
/** Resolve a `pipelex-storage://` URI to a viewable URL (stored-file preview). */
|
|
52
|
+
resolveUrl?: (uri: string) => Promise<string | null>;
|
|
53
|
+
/**
|
|
54
|
+
* Where the field can take a file the host already stores. When given, the
|
|
55
|
+
* empty field offers "Choose from your team's files" under its own controls,
|
|
56
|
+
* and the chosen file is written through `onChange` as `{ url, filename }`,
|
|
57
|
+
* the value an upload writes. See docs/sources.md.
|
|
58
|
+
*/
|
|
59
|
+
fileSource?: FileSource;
|
|
60
|
+
error?: string;
|
|
61
|
+
disabled?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/** Document variant: a file shown as a PDF preview (or chip for other docs). */
|
|
64
|
+
declare function DocumentField(props: FileFieldProps): react.JSX.Element;
|
|
65
|
+
/** Image variant: a file shown as a real image preview. */
|
|
66
|
+
declare function ImageField(props: FileFieldProps): react.JSX.Element;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The moves a host makes to a list's rows, and what a move does to an id.
|
|
70
|
+
*
|
|
71
|
+
* A field's id is its value path, so a row's id is its position (`cvs.1`), and
|
|
72
|
+
* moving rows renumbers them. Two parties hold something by row and must move
|
|
73
|
+
* it when the rows move: the host, which keys what it knows about each upload
|
|
74
|
+
* by id, and the list control, which keys each row's React element by an
|
|
75
|
+
* identity of its own so the row's controls travel with it. `useMethodForm`
|
|
76
|
+
* makes every move itself (`removeItem`, `moveItem`), re-keys its own records
|
|
77
|
+
* as it does, and hands the list a log of those moves (`FieldEnv.rowMoves`),
|
|
78
|
+
* which the list replays on its row identities. One log, read by both, is what
|
|
79
|
+
* keeps them agreeing about which row is which. See docs/upload-seam.md, "What
|
|
80
|
+
* the form keeps by id moves with the rows it moves".
|
|
81
|
+
*/
|
|
82
|
+
/** One move of a list's rows, as a host records it for the list to replay. */
|
|
83
|
+
interface RowMove {
|
|
84
|
+
/**
|
|
85
|
+
* The move's number: each move is numbered above the one before it, for as
|
|
86
|
+
* long as the host lives, so a list knows which moves it has replayed.
|
|
87
|
+
*/
|
|
88
|
+
seq: number;
|
|
89
|
+
/** The list's id, the one its rows' ids extend (`cvs` for `cvs.1`). */
|
|
90
|
+
listId: string;
|
|
91
|
+
/**
|
|
92
|
+
* For each row of the list after the move, the index that row held before
|
|
93
|
+
* it. A row whose index is left out was removed.
|
|
94
|
+
*/
|
|
95
|
+
order: readonly number[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Ambient state that any field in the tree may need, threaded unchanged through
|
|
100
|
+
* the recursion so containers don't have to re-plumb it for every child.
|
|
101
|
+
*/
|
|
102
|
+
interface FieldEnv {
|
|
103
|
+
disabled?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* A file field asks its parent to upload; the parent sets the value later.
|
|
106
|
+
*
|
|
107
|
+
* It may return the upload: a promise of what it stored (`UploadedFile`),
|
|
108
|
+
* whose URL is the one the host writes at the id, rejected when the upload
|
|
109
|
+
* writes nothing. The field then knows its upload's own result by that URL,
|
|
110
|
+
* so the dropped file's preview is kept for it whichever render the host
|
|
111
|
+
* writes it in. A host that returns nothing must write the result in the
|
|
112
|
+
* render it takes the id out of `uploadingIds`. `useMethodForm`'s `dropFile`
|
|
113
|
+
* returns its upload. See docs/upload-seam.md, "The local preview belongs to
|
|
114
|
+
* the value it was made for".
|
|
115
|
+
*
|
|
116
|
+
* Its presence is the capability. A host that supplies it gets a dropzone on
|
|
117
|
+
* every file field; a host that does not gets no dropzone and no file picker,
|
|
118
|
+
* because a file taken with nowhere to go used to be dropped without a word.
|
|
119
|
+
* See docs/upload-seam.md, "Which ways into a file value a host offers".
|
|
120
|
+
*/
|
|
121
|
+
onDropFile?: (id: string, file: File) => void | PromiseLike<UploadedFile | void>;
|
|
122
|
+
/**
|
|
123
|
+
* Whether a file field offers a link input, the way in that needs no upload.
|
|
124
|
+
* `true` when absent; `false` hides the "paste a URL instead" toggle and the
|
|
125
|
+
* input behind it on every file field.
|
|
126
|
+
*
|
|
127
|
+
* It governs that input and nothing else. A file value is a URL whichever way
|
|
128
|
+
* it arrived, so this says nothing about which values are valid: a web link
|
|
129
|
+
* the host writes is still shown on the field's card and can still be
|
|
130
|
+
* cleared. Setting it to `false` with no `onDropFile` leaves a file field no
|
|
131
|
+
* way in at all, and the field throws, naming its path, when it renders.
|
|
132
|
+
*/
|
|
133
|
+
allowUrl?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Removes a list's row on the host's side: `listId` is the list's id, the
|
|
136
|
+
* one its rows' ids extend (`cvs` for `cvs.1`), and `index` the row's.
|
|
137
|
+
*
|
|
138
|
+
* Its presence is the capability, and it is the host saying it follows
|
|
139
|
+
* rows. A list hands each removal to it instead of writing the shorter list
|
|
140
|
+
* through `onChange`, so the host removes the row and, in the same update,
|
|
141
|
+
* moves to its new id whatever it keeps by the id of a later row (an entry
|
|
142
|
+
* of `uploadErrors`, an upload in flight) and drops what it kept for the
|
|
143
|
+
* removed one. The list then holds back none of the host's `uploadErrors`,
|
|
144
|
+
* which it does for a host without this callback, since that host is
|
|
145
|
+
* handed only the shorter list and cannot re-key them. A host that also
|
|
146
|
+
* gives `rowMoves` records the removal there, and the list moves its rows'
|
|
147
|
+
* identities from that record alone. `useMethodForm`'s `removeItem` is this
|
|
148
|
+
* callback. See docs/upload-seam.md, "A list does not move a failure onto
|
|
149
|
+
* another row".
|
|
150
|
+
*/
|
|
151
|
+
onRemoveItem?: (listId: string, index: number) => void;
|
|
152
|
+
/**
|
|
153
|
+
* Every move the host has made to a list's rows, oldest first, each
|
|
154
|
+
* numbered above the one before it (`RowMove`).
|
|
155
|
+
*
|
|
156
|
+
* A list keys each row's controls by an identity of its own, so that a row
|
|
157
|
+
* keeps them (a caret, a dropped file's preview, a dismissed failure) when
|
|
158
|
+
* an earlier row goes. It moves those identities itself for a removal it
|
|
159
|
+
* writes through `onChange`; for everything else a host does to the rows it
|
|
160
|
+
* would see only a new list, and match rows by position. Given this, a list
|
|
161
|
+
* replays every move made to it since it last rendered, the ones made to an
|
|
162
|
+
* outer list's rows included, which rename it, and moves no identity of its
|
|
163
|
+
* own accord: a host giving it records every move it makes here, a removal
|
|
164
|
+
* handed to `onRemoveItem` included. `useMethodForm`'s `rowMoves` is this
|
|
165
|
+
* log. See docs/upload-seam.md, "What the form keeps by id moves with the
|
|
166
|
+
* rows it moves".
|
|
167
|
+
*/
|
|
168
|
+
rowMoves?: readonly RowMove[];
|
|
169
|
+
/** Ids currently mid-upload. */
|
|
170
|
+
uploadingIds?: ReadonlySet<string>;
|
|
171
|
+
/**
|
|
172
|
+
* Why an upload failed, keyed by the same id `onDropFile` was handed, so the
|
|
173
|
+
* message shows on the field that took the file rather than under the whole
|
|
174
|
+
* form, where only its wording could say which input failed.
|
|
175
|
+
*
|
|
176
|
+
* The map is the host's truth about each upload attempt: a host removes an
|
|
177
|
+
* entry when the next file is dropped at that id, and sets it again if that
|
|
178
|
+
* upload fails too. A clear or a typed link on the field hides the message
|
|
179
|
+
* until the host sends a different one or removes it, and a list holds back
|
|
180
|
+
* the entries a row removal left at a renumbered position, unless the host
|
|
181
|
+
* re-keys them itself through `onRemoveItem`. A host should
|
|
182
|
+
* also remove an entry once another file, or none, is written at its id, so
|
|
183
|
+
* that a control mounted there later shows no failure under it:
|
|
184
|
+
* `useMethodForm` does this for every write of its values.
|
|
185
|
+
*/
|
|
186
|
+
uploadErrors?: ReadonlyMap<string, string>;
|
|
187
|
+
/** Resolve a `pipelex-storage://` URI to a browser-viewable URL (for previews
|
|
188
|
+
* of already-stored files). */
|
|
189
|
+
resolveUrl?: (uri: string) => Promise<string | null>;
|
|
190
|
+
/**
|
|
191
|
+
* Where a file field can take a file the host already stores. Its presence
|
|
192
|
+
* is the capability, as `onDropFile`'s is: every empty file field then
|
|
193
|
+
* offers "Choose from your team's files", and a list of files offers it for
|
|
194
|
+
* several at once. The choice is written as `{ url, filename }`, exactly as
|
|
195
|
+
* an upload's result is. See docs/sources.md.
|
|
196
|
+
*/
|
|
197
|
+
fileSource?: FileSource;
|
|
198
|
+
}
|
|
199
|
+
interface FieldRendererProps {
|
|
200
|
+
field: RunField;
|
|
201
|
+
value: unknown;
|
|
202
|
+
onChange: (value: unknown) => void;
|
|
203
|
+
/** Unique, stable id used for label linkage and upload tracking. */
|
|
204
|
+
id: string;
|
|
205
|
+
error?: string;
|
|
206
|
+
env?: FieldEnv;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The single dispatch point: a normalized `RunField` → the matching control.
|
|
210
|
+
* Object and list fields recurse back through here, so the whole form is one
|
|
211
|
+
* data-driven tree with no per-type branching anywhere else.
|
|
212
|
+
*/
|
|
213
|
+
declare function FieldRenderer({ field, value, onChange, id, error, env }: FieldRendererProps): react.JSX.Element;
|
|
214
|
+
|
|
215
|
+
export { DocumentField as D, type FieldEnv as F, ImageField as I, type RowMove as R, type UploadedFile as U, FieldRenderer as a, type FieldRendererProps as b, type FileValue as c };
|
|
@@ -9,7 +9,7 @@ import { Components, Renderer } from '@json-render/react';
|
|
|
9
9
|
import { B as BrandManifest } from '../manifest-DUeAWeqa.js';
|
|
10
10
|
export { b as brandManifestSchema } from '../manifest-DUeAWeqa.js';
|
|
11
11
|
import * as zod_v4_core from 'zod/v4/core';
|
|
12
|
-
import { F as FieldEnv } from '../field-renderer-
|
|
12
|
+
import { F as FieldEnv } from '../field-renderer-lNhtw-Cz.js';
|
|
13
13
|
export { seedInputs } from '../core/seed.js';
|
|
14
14
|
import '../core/contracts.js';
|
|
15
15
|
import 'mthds/protocol';
|
package/dist/generative/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn, fieldControlClass, ResultRoot, ResultField, FieldRenderer, ToggleGroup, ToggleGroupItem, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../chunk-
|
|
2
|
+
import { cn, brandInkClass, brandFillClass, brandFillInkClass, fieldControlClass, ResultRoot, ResultField, FieldRenderer, ToggleGroup, ToggleGroupItem, brandFillStrongClass, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../chunk-5IZWP2X4.js';
|
|
3
3
|
import '../chunk-ICUEHNEH.js';
|
|
4
4
|
export { seedInputs } from '../chunk-VH5CDQDR.js';
|
|
5
5
|
import '../chunk-EQJBM3NS.js';
|
|
@@ -2473,7 +2473,7 @@ function Steps({ props, children }) {
|
|
|
2473
2473
|
{
|
|
2474
2474
|
className: cn(
|
|
2475
2475
|
"flex h-6 w-6 items-center justify-center rounded-full text-[12px] font-semibold tabular-nums",
|
|
2476
|
-
position < index ?
|
|
2476
|
+
position < index ? cn(brandFillStrongClass, brandFillInkClass) : position === index ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
|
2477
2477
|
),
|
|
2478
2478
|
children: position + 1
|
|
2479
2479
|
}
|
|
@@ -2744,13 +2744,29 @@ function AppBar({ props }) {
|
|
|
2744
2744
|
},
|
|
2745
2745
|
link
|
|
2746
2746
|
)) }) : null,
|
|
2747
|
-
props.tag ? /* @__PURE__ */ jsx(
|
|
2747
|
+
props.tag ? /* @__PURE__ */ jsx(
|
|
2748
|
+
"span",
|
|
2749
|
+
{
|
|
2750
|
+
className: cn(
|
|
2751
|
+
"min-w-0 truncate rounded-lg border border-primary/40 px-2.5 py-1 font-mono text-[11px]",
|
|
2752
|
+
brandFillClass,
|
|
2753
|
+
brandFillInkClass
|
|
2754
|
+
),
|
|
2755
|
+
children: props.tag
|
|
2756
|
+
}
|
|
2757
|
+
) : null
|
|
2748
2758
|
] })
|
|
2749
2759
|
] }) });
|
|
2750
2760
|
}
|
|
2751
2761
|
function Hero({ props }) {
|
|
2752
2762
|
return /* @__PURE__ */ jsxs("section", { className: "pt-12 pb-10 sm:pt-14 sm:pb-12", children: [
|
|
2753
|
-
props.eyebrow ? /* @__PURE__ */ jsx(
|
|
2763
|
+
props.eyebrow ? /* @__PURE__ */ jsx(
|
|
2764
|
+
"p",
|
|
2765
|
+
{
|
|
2766
|
+
className: cn("mb-4 text-[11px] font-medium tracking-[0.15em] uppercase", brandInkClass),
|
|
2767
|
+
children: props.eyebrow
|
|
2768
|
+
}
|
|
2769
|
+
) : null,
|
|
2754
2770
|
/* @__PURE__ */ jsx("h1", { className: "max-w-2xl text-4xl font-bold tracking-tight text-foreground sm:text-[44px] sm:leading-[1.15]", children: props.headline }),
|
|
2755
2771
|
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-4 max-w-2xl text-[17px] leading-relaxed text-muted-foreground", children: props.lede }) : null
|
|
2756
2772
|
] });
|
|
@@ -2797,7 +2813,7 @@ function Section({ props, children }) {
|
|
|
2797
2813
|
return /* @__PURE__ */ jsxs("section", { className: "border-t border-border py-9 first:border-t-0 first:pt-0", children: [
|
|
2798
2814
|
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
2799
2815
|
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3", children: [
|
|
2800
|
-
props.number ? /* @__PURE__ */ jsx("span", { className: "font-mono text-[12px]
|
|
2816
|
+
props.number ? /* @__PURE__ */ jsx("span", { className: cn("font-mono text-[12px]", brandInkClass), children: props.number }) : null,
|
|
2801
2817
|
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight text-foreground", children: props.title })
|
|
2802
2818
|
] }),
|
|
2803
2819
|
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: props.lede }) : null
|