@pipelex/mthds-form 0.19.0 → 0.21.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 +4 -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-QCGIH3WY.js → chunk-3XDR2C4I.js} +1473 -111
- package/dist/chunk-3XDR2C4I.js.map +1 -0
- package/dist/{chunk-2HIYTIQ6.js → chunk-4DPYGNJ3.js} +3 -3
- package/dist/{chunk-2HIYTIQ6.js.map → chunk-4DPYGNJ3.js.map} +1 -1
- package/dist/{chunk-HQMXXK5X.js → chunk-E6BIXJ6C.js} +3 -3
- package/dist/{chunk-HQMXXK5X.js.map → chunk-E6BIXJ6C.js.map} +1 -1
- package/dist/chunk-EQJBM3NS.js +41 -0
- package/dist/chunk-EQJBM3NS.js.map +1 -0
- package/dist/{chunk-MEXWXLR7.js → chunk-ZX52CQJ4.js} +16 -4
- package/dist/chunk-ZX52CQJ4.js.map +1 -0
- package/dist/core/gate.js +2 -2
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +5 -4
- package/dist/core/sources.d.ts +161 -0
- package/dist/core/sources.js +3 -0
- package/dist/core/sources.js.map +1 -0
- package/dist/core/values.d.ts +25 -1
- package/dist/core/values.js +2 -2
- package/dist/core/wire-format.d.ts +11 -1
- package/dist/core/wire-format.js +1 -1
- package/dist/{field-renderer-C5Onx6qB.d.ts → field-renderer-B3g16qZT.d.ts} +9 -0
- package/dist/generative/index.d.ts +2 -1
- package/dist/generative/index.js +24 -7
- package/dist/generative/index.js.map +1 -1
- package/dist/react/index.d.ts +248 -6
- package/dist/react/index.js +4 -3
- package/dist/styles.css +1 -1
- package/package.json +4 -1
- package/dist/chunk-MEXWXLR7.js.map +0 -1
- package/dist/chunk-QCGIH3WY.js.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two optional host seams beside the upload: a **file source**, which lets
|
|
3
|
+
* a file field take a file the host already stores, and an **example source**,
|
|
4
|
+
* which lets a whole form take a set of values someone saved for the method.
|
|
5
|
+
*
|
|
6
|
+
* The kernel states the shapes and applies the answers; the host answers. It
|
|
7
|
+
* knows nothing of any platform's routes, records or permissions: a host maps
|
|
8
|
+
* its own list onto these shapes, and a host that gives neither seam gets the
|
|
9
|
+
* form it had before. The controls that use them are `./react`'s
|
|
10
|
+
* (`FilePicker`, `FillWithExample`), and docs/sources.md is the contract.
|
|
11
|
+
*
|
|
12
|
+
* Shapes and pure functions only, so a host's server can import the types it
|
|
13
|
+
* answers with, and the rules that write an answer into a value are tested in
|
|
14
|
+
* node. No React, no fetch.
|
|
15
|
+
*/
|
|
16
|
+
/** Whose files a file source lists: the person's own, or the whole team's. */
|
|
17
|
+
type FileSourceScope = 'mine' | 'everyone';
|
|
18
|
+
/** One page of a file source's list, as the picker asks for it. */
|
|
19
|
+
interface FileSourceQuery {
|
|
20
|
+
/** What the person searched for, trimmed; empty when they searched for nothing. */
|
|
21
|
+
search: string;
|
|
22
|
+
/** Whose files to list. */
|
|
23
|
+
scope: FileSourceScope;
|
|
24
|
+
/**
|
|
25
|
+
* The MIME types the field accepts, after any narrowing a host applied
|
|
26
|
+
* (`narrowFileFormats`), in the field's own order. The list must hold only
|
|
27
|
+
* files of these types: the picker writes what is chosen straight into the
|
|
28
|
+
* field, and a field that refuses a dropped file of another type must not
|
|
29
|
+
* be handed one this way.
|
|
30
|
+
*/
|
|
31
|
+
mediaTypes: readonly string[];
|
|
32
|
+
/** Where to continue: absent for the first page, else the previous page's `nextCursor`. */
|
|
33
|
+
cursor?: string;
|
|
34
|
+
/** Aborted when the answer is no longer wanted: a newer search, a closed picker. */
|
|
35
|
+
signal: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
/** One row of a file source's list: a file the host already stores. */
|
|
38
|
+
interface SourceFile {
|
|
39
|
+
/** The row's identity in the host's list, which a choice is kept by. */
|
|
40
|
+
id: string;
|
|
41
|
+
/**
|
|
42
|
+
* The stored file's address, written into the field as its `url`, exactly as
|
|
43
|
+
* an upload's result is. Several rows may share one address.
|
|
44
|
+
*/
|
|
45
|
+
url: string;
|
|
46
|
+
/** The file's name, shown on the row and written into the field as its `filename`. */
|
|
47
|
+
name: string;
|
|
48
|
+
/** The file's MIME type, which names its format on the row. */
|
|
49
|
+
mediaType?: string;
|
|
50
|
+
/** The file's size in bytes. */
|
|
51
|
+
size?: number;
|
|
52
|
+
/** How the file came to be. A `test` file is marked "Test file" at the head of its row's second line. */
|
|
53
|
+
kind?: 'uploaded' | 'generated' | 'test';
|
|
54
|
+
/** When the file was stored, as an ISO 8601 timestamp; the row says it as a date. */
|
|
55
|
+
createdAt?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Where the file came from, in the host's own words ("Uploaded by Ana",
|
|
58
|
+
* "Made by Invoice extraction"), shown on the row's second line as it is.
|
|
59
|
+
*/
|
|
60
|
+
origin?: string;
|
|
61
|
+
}
|
|
62
|
+
/** One page of a file source's answer. */
|
|
63
|
+
interface FileSourcePage {
|
|
64
|
+
/** The page's files, newest first. A page may be empty and still carry a cursor. */
|
|
65
|
+
files: readonly SourceFile[];
|
|
66
|
+
/** Where the next page starts, or nothing when this page is the last. */
|
|
67
|
+
nextCursor?: string | null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Where a file field can take a file the host already stores. When a form has
|
|
71
|
+
* one, every file field offers "Choose from your team's files", and a list of
|
|
72
|
+
* files offers it for several at once.
|
|
73
|
+
*/
|
|
74
|
+
interface FileSource {
|
|
75
|
+
/** Lists one page of files for a query. Rejecting shows the failure with Try again. */
|
|
76
|
+
list: (query: FileSourceQuery) => Promise<FileSourcePage>;
|
|
77
|
+
/** Which of Mine and Everyone the picker opens on. */
|
|
78
|
+
initialScope: FileSourceScope;
|
|
79
|
+
}
|
|
80
|
+
/** One example of a method: a name and the inputs a run of it sends. */
|
|
81
|
+
interface FormExample {
|
|
82
|
+
/** The example's identity in the host's list. */
|
|
83
|
+
id: string;
|
|
84
|
+
/** The example's name, shown on its row. */
|
|
85
|
+
name: string;
|
|
86
|
+
/**
|
|
87
|
+
* The inputs as a run takes them: each input `{ concept, content }`, an
|
|
88
|
+
* optional input left out, an empty plural input a bare `[]`. It is the
|
|
89
|
+
* shape `apiInputsFromRunValues` produces, so a host saves an example from
|
|
90
|
+
* `form.inputs` and hands it back as it is.
|
|
91
|
+
*/
|
|
92
|
+
inputs: Readonly<Record<string, unknown>>;
|
|
93
|
+
}
|
|
94
|
+
/** One page of an example source's list. */
|
|
95
|
+
interface ExampleSourceQuery {
|
|
96
|
+
/** Where to continue: absent for the first page, else the previous page's `nextCursor`. */
|
|
97
|
+
cursor?: string;
|
|
98
|
+
/** Aborted when the answer is no longer wanted. */
|
|
99
|
+
signal: AbortSignal;
|
|
100
|
+
}
|
|
101
|
+
/** One page of an example source's answer. */
|
|
102
|
+
interface ExampleSourcePage {
|
|
103
|
+
/** The page's examples, in the order the list shows them. */
|
|
104
|
+
examples: readonly FormExample[];
|
|
105
|
+
/** Where the next page starts, or nothing when this page is the last. */
|
|
106
|
+
nextCursor?: string | null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Where a form can take a whole set of values. When a form has one, it offers
|
|
110
|
+
* "Fill with an example". `rename` and `delete` are capabilities: each is
|
|
111
|
+
* offered on a row's menu only when given, so a host gives them only to a
|
|
112
|
+
* person allowed to change examples.
|
|
113
|
+
*/
|
|
114
|
+
interface ExampleSource {
|
|
115
|
+
/** Lists one page of the method's examples. Rejecting shows the failure with Try again. */
|
|
116
|
+
list: (query: ExampleSourceQuery) => Promise<ExampleSourcePage>;
|
|
117
|
+
/**
|
|
118
|
+
* Renames an example. Resolving to the example as stored replaces the row;
|
|
119
|
+
* resolving to nothing keeps the row under the new name.
|
|
120
|
+
*/
|
|
121
|
+
rename?: (example: FormExample, name: string) => Promise<FormExample | void>;
|
|
122
|
+
/** Deletes an example. Asked for only after the person confirmed it. */
|
|
123
|
+
delete?: (example: FormExample) => Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Whether "Fill with an example" is left out while the method has no
|
|
126
|
+
* example. `false` by default, when the empty list says how to make one.
|
|
127
|
+
* A host sets it for a person who cannot save an example, to whom an empty
|
|
128
|
+
* list has nothing to offer; the form then asks for the first page as it
|
|
129
|
+
* mounts, to know whether to offer the control.
|
|
130
|
+
*/
|
|
131
|
+
hideWhenEmpty?: boolean;
|
|
132
|
+
}
|
|
133
|
+
/** What a chosen file writes into its field: the same `{ url, filename }` an upload writes. */
|
|
134
|
+
declare function sourceFileValue(file: SourceFile): {
|
|
135
|
+
url: string;
|
|
136
|
+
filename: string;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* A list of files with the chosen files written into it, in the order they
|
|
140
|
+
* were chosen: first into the rows that hold no file yet (a row the person
|
|
141
|
+
* added and left empty), then as new rows after the last. A file whose address
|
|
142
|
+
* the list already holds is not written again, and neither is a second choice
|
|
143
|
+
* of one address: two rows of a source may name the same stored file. Returns
|
|
144
|
+
* a new array and leaves `held` as it was.
|
|
145
|
+
*/
|
|
146
|
+
declare function addChosenFiles(held: readonly unknown[] | undefined, chosen: readonly SourceFile[]): unknown[];
|
|
147
|
+
/**
|
|
148
|
+
* How many files a list may still take from a source: its bound less the files
|
|
149
|
+
* it holds, since `addChosenFiles` writes into its empty rows before adding
|
|
150
|
+
* any. `undefined` when the list has no bound.
|
|
151
|
+
*/
|
|
152
|
+
/**
|
|
153
|
+
* How many files `addChosenFiles` writes for `chosen` into `held`: the choices
|
|
154
|
+
* whose address the list does not hold yet, each address once. This is what a
|
|
155
|
+
* choice spends of `roomForChosenFiles`, so a held file chosen again, or a
|
|
156
|
+
* second row naming a chosen file, takes no room.
|
|
157
|
+
*/
|
|
158
|
+
declare function chosenFilesAdded(held: readonly unknown[] | undefined, chosen: readonly SourceFile[]): number;
|
|
159
|
+
declare function roomForChosenFiles(held: readonly unknown[] | undefined, maxItemCount: number | undefined): number | undefined;
|
|
160
|
+
|
|
161
|
+
export { type ExampleSource, type ExampleSourcePage, type ExampleSourceQuery, type FileSource, type FileSourcePage, type FileSourceQuery, type FileSourceScope, type FormExample, type SourceFile, addChosenFiles, chosenFilesAdded, roomForChosenFiles, sourceFileValue };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sources.js"}
|
package/dist/core/values.d.ts
CHANGED
|
@@ -55,6 +55,30 @@ declare function storeInputDataFromRunValues(values: Dict, fields: RunField[], i
|
|
|
55
55
|
* the envelope agree; the bare form runs today and stays correct after.)
|
|
56
56
|
*/
|
|
57
57
|
declare function apiInputsFromRunValues(values: Dict, fields: RunField[], inputSchemas: InputSchemas): Record<string, unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* The form values a set of run inputs stands for: the inverse of
|
|
60
|
+
* `apiInputsFromRunValues`, for inputs saved from a form (an example) and
|
|
61
|
+
* handed back to one.
|
|
62
|
+
*
|
|
63
|
+
* Each input is read as a run sends it, `{ concept, content }`, or as its bare
|
|
64
|
+
* content (the empty plural `[]`), and its content is turned back into the
|
|
65
|
+
* control's own shape. Only an object naming its concept is read as that
|
|
66
|
+
* envelope: a structure sent bare may well have a property called `content`. An input the inputs do not hold is left empty, and an
|
|
67
|
+
* optional structure they do not hold stays closed: the inputs are the whole
|
|
68
|
+
* run, so an input they left out was left out of the run too. An input no
|
|
69
|
+
* field takes is ignored.
|
|
70
|
+
*/
|
|
71
|
+
declare function runValuesFromInputs(inputs: Readonly<Dict> | undefined, fields: RunField[]): Dict;
|
|
72
|
+
/**
|
|
73
|
+
* Whether two sets of form values send the same run.
|
|
74
|
+
*
|
|
75
|
+
* Compared in the shape a run takes, so the differences a run cannot see do
|
|
76
|
+
* not count: a text typed and then erased is the blank it started as, and an
|
|
77
|
+
* optional structure opened and left empty is the closed one it was. What
|
|
78
|
+
* "Fill with an example" asks before replacing is the work a person did, and
|
|
79
|
+
* a value that sends nothing is no work to lose.
|
|
80
|
+
*/
|
|
81
|
+
declare function sameRunValues(a: Dict, b: Dict, fields: RunField[]): boolean;
|
|
58
82
|
/** One named output pulled out of a run's working memory, ready to render. */
|
|
59
83
|
interface OutputEntry {
|
|
60
84
|
/** The output variable key (mono). */
|
|
@@ -96,4 +120,4 @@ declare function fieldsForContract(contract: PipeIOContract | undefined, descrip
|
|
|
96
120
|
*/
|
|
97
121
|
declare function setValueAtPath(root: Dict, path: string[], value: unknown): Dict;
|
|
98
122
|
|
|
99
|
-
export { type OutputEntry, apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues };
|
|
123
|
+
export { type OutputEntry, apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromInputs, runValuesFromStore, sameRunValues, setValueAtPath, storeInputDataFromRunValues };
|
package/dist/core/values.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromStore, setValueAtPath, storeInputDataFromRunValues } from '../chunk-
|
|
1
|
+
export { apiInputsFromRunValues, fieldsForContract, inputDataFromWorkingMemory, outputsFromPipeOutput, rjsfDataFromRunValues, runValuesFromInputs, runValuesFromStore, sameRunValues, setValueAtPath, storeInputDataFromRunValues } from '../chunk-ZX52CQJ4.js';
|
|
2
2
|
import '../chunk-ZB4RYUKV.js';
|
|
3
3
|
import '../chunk-XCO6GTEC.js';
|
|
4
|
-
import '../chunk-
|
|
4
|
+
import '../chunk-E6BIXJ6C.js';
|
|
5
5
|
import '../chunk-XO5OENNF.js';
|
|
6
6
|
import '../chunk-OD3VYXUY.js';
|
|
7
7
|
import '../chunk-WYH56SOZ.js';
|
|
@@ -40,6 +40,16 @@ declare function deflateInput(value: unknown, conceptCode: string): unknown;
|
|
|
40
40
|
* - { concept, content } → content (for custom concepts)
|
|
41
41
|
*/
|
|
42
42
|
declare function inflateInput(value: unknown, conceptCode: string): unknown;
|
|
43
|
+
/**
|
|
44
|
+
* Detect the fully-qualified wrapper form `{ concept: "...", content: ... }`.
|
|
45
|
+
* pipelex accepts this for any concept (native or custom), and the mthds-inputs
|
|
46
|
+
* agent skill writes it by default for disambiguation. We unwrap before applying
|
|
47
|
+
* concept-specific inflation so the form populates correctly.
|
|
48
|
+
*/
|
|
49
|
+
declare function isWrappedForm(value: unknown): value is {
|
|
50
|
+
concept: string;
|
|
51
|
+
content: unknown;
|
|
52
|
+
};
|
|
43
53
|
interface InputSchema {
|
|
44
54
|
concept_ref: string;
|
|
45
55
|
json_schema?: Record<string, unknown>;
|
|
@@ -92,4 +102,4 @@ declare function pruneEmptyOptionals(value: unknown, rawSchema: Record<string, u
|
|
|
92
102
|
*/
|
|
93
103
|
declare function inflateAllInputs(simplifiedData: Record<string, unknown>, inputSchemas: Record<string, InputSchema>): Record<string, unknown>;
|
|
94
104
|
|
|
95
|
-
export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, pruneEmptyOptionals, resolveConceptCode };
|
|
105
|
+
export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, isWrappedForm, pruneEmptyOptionals, resolveConceptCode };
|
package/dist/core/wire-format.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, pruneEmptyOptionals, resolveConceptCode } from '../chunk-
|
|
1
|
+
export { deflateAllInputs, deflateInput, healStringWrappers, inflateAllInputs, inflateInput, isWrappedForm, pruneEmptyOptionals, resolveConceptCode } from '../chunk-E6BIXJ6C.js';
|
|
2
2
|
import '../chunk-OD3VYXUY.js';
|
|
3
3
|
import '../chunk-V3OV2XQT.js';
|
|
4
4
|
import '../chunk-GKV77RAS.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { RunField } from './core/descriptor.js';
|
|
3
|
+
import { FileSource } from './core/sources.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Ambient state that any field in the tree may need, threaded unchanged through
|
|
@@ -45,6 +46,14 @@ interface FieldEnv {
|
|
|
45
46
|
/** Resolve a `pipelex-storage://` URI to a browser-viewable URL (for previews
|
|
46
47
|
* of already-stored files). */
|
|
47
48
|
resolveUrl?: (uri: string) => Promise<string | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Where a file field can take a file the host already stores. Its presence
|
|
51
|
+
* is the capability, as `onDropFile`'s is: every empty file field then
|
|
52
|
+
* offers "Choose from your team's files", and a list of files offers it for
|
|
53
|
+
* several at once. The choice is written as `{ url, filename }`, exactly as
|
|
54
|
+
* an upload's result is. See docs/sources.md.
|
|
55
|
+
*/
|
|
56
|
+
fileSource?: FileSource;
|
|
48
57
|
}
|
|
49
58
|
interface FieldRendererProps {
|
|
50
59
|
field: RunField;
|
|
@@ -9,11 +9,12 @@ 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-B3g16qZT.js';
|
|
13
13
|
export { seedInputs } from '../core/seed.js';
|
|
14
14
|
import '../core/contracts.js';
|
|
15
15
|
import 'mthds/protocol';
|
|
16
16
|
import '../core/file-formats.js';
|
|
17
|
+
import '../core/sources.js';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Whether a spec is one this catalog can render - the check every source
|
package/dist/generative/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
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-3XDR2C4I.js';
|
|
3
3
|
import '../chunk-ICUEHNEH.js';
|
|
4
4
|
export { seedInputs } from '../chunk-VH5CDQDR.js';
|
|
5
|
+
import '../chunk-EQJBM3NS.js';
|
|
5
6
|
import '../chunk-RRTI7AIR.js';
|
|
6
|
-
import '../chunk-
|
|
7
|
+
import '../chunk-ZX52CQJ4.js';
|
|
7
8
|
import { isNativeValueNode, readDateContent, formatDateContent, isNativeHtmlNode, isNativeDateNode, isNativeCompositeNode } from '../chunk-CM4KVG53.js';
|
|
8
9
|
export { brandManifestSchema } from '../chunk-QFX332LE.js';
|
|
9
10
|
import '../chunk-ZB4RYUKV.js';
|
|
10
11
|
import '../chunk-FABY3DEY.js';
|
|
11
12
|
import '../chunk-XCO6GTEC.js';
|
|
12
|
-
import '../chunk-
|
|
13
|
+
import '../chunk-E6BIXJ6C.js';
|
|
13
14
|
import '../chunk-XO5OENNF.js';
|
|
14
15
|
import '../chunk-OD3VYXUY.js';
|
|
15
16
|
import '../chunk-WYH56SOZ.js';
|
|
@@ -2472,7 +2473,7 @@ function Steps({ props, children }) {
|
|
|
2472
2473
|
{
|
|
2473
2474
|
className: cn(
|
|
2474
2475
|
"flex h-6 w-6 items-center justify-center rounded-full text-[12px] font-semibold tabular-nums",
|
|
2475
|
-
position < index ?
|
|
2476
|
+
position < index ? cn(brandFillStrongClass, brandFillInkClass) : position === index ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
|
2476
2477
|
),
|
|
2477
2478
|
children: position + 1
|
|
2478
2479
|
}
|
|
@@ -2743,13 +2744,29 @@ function AppBar({ props }) {
|
|
|
2743
2744
|
},
|
|
2744
2745
|
link
|
|
2745
2746
|
)) }) : null,
|
|
2746
|
-
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
|
|
2747
2758
|
] })
|
|
2748
2759
|
] }) });
|
|
2749
2760
|
}
|
|
2750
2761
|
function Hero({ props }) {
|
|
2751
2762
|
return /* @__PURE__ */ jsxs("section", { className: "pt-12 pb-10 sm:pt-14 sm:pb-12", children: [
|
|
2752
|
-
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,
|
|
2753
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 }),
|
|
2754
2771
|
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-4 max-w-2xl text-[17px] leading-relaxed text-muted-foreground", children: props.lede }) : null
|
|
2755
2772
|
] });
|
|
@@ -2796,7 +2813,7 @@ function Section({ props, children }) {
|
|
|
2796
2813
|
return /* @__PURE__ */ jsxs("section", { className: "border-t border-border py-9 first:border-t-0 first:pt-0", children: [
|
|
2797
2814
|
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
2798
2815
|
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3", children: [
|
|
2799
|
-
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,
|
|
2800
2817
|
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight text-foreground", children: props.title })
|
|
2801
2818
|
] }),
|
|
2802
2819
|
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: props.lede }) : null
|