@fungi.computer/caps 0.3.1 → 0.5.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.
@@ -33,11 +33,11 @@ Props accepted by ReleaseTag.
33
33
 
34
34
  #### Type Declaration
35
35
 
36
- | Name | Type | Description |
37
- | --------- | ------------------------------------------------ | --------------------------------------------------------------------------- |
38
- | `title` | readonly `string`[] | One or two short lines printed large on the tag, such as ["Fungi", "Beta"]. |
39
- | `label?` | `ReactNode` | A small accent strip under the title, such as "Update". |
40
- | `entries` | readonly [`ReleaseTagEntry`](#releasetagentry)[] | - |
36
+ | Name | Type | Description |
37
+ | ---------- | ------------------------------------------------ | --------------------------------------------------------------------------- |
38
+ | `title` | readonly `string`[] | One or two short lines printed large on the tag, such as ["Fungi", "Beta"]. |
39
+ | `label?` | `ReactNode` | A small accent strip under the title, such as "Update". |
40
+ | `entries?` | readonly [`ReleaseTagEntry`](#releasetagentry)[] | Names and versions ruled under the page; omit for a tag with none. |
41
41
 
42
42
  ## Functions
43
43
 
@@ -48,8 +48,8 @@ function ReleaseTag(__namedParameters): Element;
48
48
  ```
49
49
 
50
50
  A release badge: a torn-off calendar page with binder rings, a stacked title,
51
- one accent strip and a ruled list of names and versions below. Presentation
52
- only; the caller supplies every word.
51
+ one accent strip and an optional ruled list of names and versions below.
52
+ Presentation only; the caller supplies every word.
53
53
 
54
54
  #### Parameters
55
55
 
@@ -45,16 +45,17 @@ Controlled selection and structural slots for a row of footer trays.
45
45
 
46
46
  #### Type Declaration
47
47
 
48
- | Name | Type | Description |
49
- | ---------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
- | `items` | readonly [`StatusTrayItem`](#statustrayitem)[] | Readouts and their tray contents in display order. |
51
- | `openId` | `string` \| `null` | Which tray is open. The host owns this state. |
52
- | `onOpenChange()` | (`id`) => `void` | Requests a selection change or dismissal. |
53
- | `leading?` | `ReactNode` | Leading readouts that never open a tray (connection state, etc.). |
54
- | `trailing?` | `ReactNode` | Trailing controls after the chips (Send, Expand, …). |
55
- | `placement?` | `"housing"` \| `"screen"` \| `"host"` | Structural seat. `housing` sits on the plastic below a screen; `screen` sits inside a Display as flat, screen-native text (the wiki's screen material rule); `host` means a host's own chrome draws the chips, so only the open tray renders here. The look of each seat is theme-owned. |
56
- | `align?` | `"start"` \| `"end"` | Which edge the tray aligns to. |
57
- | `hostInset?` | `number` | For `host` placement: where the host's trigger starts, in CSS pixels from this tray's inline start, so the open tray sits above it. |
48
+ | Name | Type | Description |
49
+ | ------------------ | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
+ | `items` | readonly [`StatusTrayItem`](#statustrayitem)[] | Readouts and their tray contents in display order. |
51
+ | `openId` | `string` \| `null` | Which tray is open. The host owns this state. |
52
+ | `onOpenChange()` | (`id`) => `void` | Requests a selection change or dismissal. |
53
+ | `leading?` | `ReactNode` | Leading readouts that never open a tray (connection state, etc.). |
54
+ | `trailing?` | `ReactNode` | Trailing controls after the chips (Send, Expand, …). |
55
+ | `renderTrigger()?` | (`props`) => `ReactNode` | Compose a native button trigger, forwarding these attributes, children and ref. StatusTray still owns disclosure, dismissal and return focus. Omit to retain the default chip and its theme-owned finish. |
56
+ | `placement?` | `"housing"` \| `"screen"` \| `"host"` | Structural seat. `housing` sits on the plastic below a screen; `screen` sits inside a Display as flat, screen-native text (the wiki's screen material rule); `host` means a host's own chrome draws the chips, so only the open tray renders here. The look of each seat is theme-owned. |
57
+ | `align?` | `"start"` \| `"end"` | Which edge the tray aligns to. |
58
+ | `hostInset?` | `number` | For `host` placement: where the host's trigger starts, in CSS pixels from this tray's inline start, so the open tray sits above it. |
58
59
 
59
60
  ## Functions
60
61
 
@@ -39,9 +39,10 @@ Props accepted by Form.
39
39
 
40
40
  #### Type Declaration
41
41
 
42
- | Name | Type | Description |
43
- | ------- | --------- | ------------------------------------------------------- |
44
- | `busy?` | `boolean` | Disables every control while a submission is in flight. |
42
+ | Name | Type | Description |
43
+ | --------- | ----------- | ----------------------------------------------------------------- |
44
+ | `busy?` | `boolean` | Disables every control while a submission is in flight. |
45
+ | `layout?` | `"columns"` | Opt-in label/control grid; the default field rhythm is unchanged. |
45
46
 
46
47
  ---
47
48
 
@@ -55,12 +56,13 @@ Props accepted by FormSection.
55
56
 
56
57
  #### Type Declaration
57
58
 
58
- | Name | Type | Description |
59
- | -------------- | ----------------- | -------------------------------------- |
60
- | `title` | `ReactNode` | - |
61
- | `description?` | `ReactNode` | - |
62
- | `level?` | `2` \| `3` \| `4` | Heading level inside the page outline. |
63
- | `children` | `ReactNode` | - |
59
+ | Name | Type | Description |
60
+ | -------------- | ----------------- | ---------------------------------------------------------- |
61
+ | `title` | `ReactNode` | - |
62
+ | `description?` | `ReactNode` | - |
63
+ | `layout?` | `"columns"` | Opt-in label/control grid when used without a Form parent. |
64
+ | `level?` | `2` \| `3` \| `4` | Heading level inside the page outline. |
65
+ | `children` | `ReactNode` | - |
64
66
 
65
67
  ---
66
68
 
@@ -78,6 +80,7 @@ Props accepted by FieldSet.
78
80
  | -------------- | ----------- | ----------------------------------------------------------------- |
79
81
  | `legend` | `ReactNode` | - |
80
82
  | `description?` | `ReactNode` | - |
83
+ | `layout?` | `"columns"` | Opt-in label/control grid when used without a Form parent. |
81
84
  | `error?` | `ReactNode` | The group's announced error, such as "Choose at least one right". |
82
85
  | `children` | `ReactNode` | - |
83
86
 
@@ -24,7 +24,16 @@ Where a composer's send action is: nothing to send, ready, or working.
24
24
 
25
25
  ```ts
26
26
  type SendActionProps = object &
27
- Omit<ButtonProps, "children" | "size" | "loading" | "aria-label">;
27
+ Omit<
28
+ KeycapButtonProps,
29
+ | "label"
30
+ | "icon"
31
+ | "readout"
32
+ | "shortcut"
33
+ | "pressed"
34
+ | "expanded"
35
+ | "aria-label"
36
+ >;
28
37
  ```
29
38
 
30
39
  Props accepted by SendAction.
@@ -46,13 +55,23 @@ Props accepted by SendAction.
46
55
  ```ts
47
56
  const SendAction: ForwardRefExoticComponent<
48
57
  object &
49
- Omit<ButtonProps, "aria-label" | "children" | "size" | "loading"> &
58
+ Omit<
59
+ KeycapButtonProps,
60
+ | "aria-label"
61
+ | "label"
62
+ | "icon"
63
+ | "readout"
64
+ | "expanded"
65
+ | "pressed"
66
+ | "shortcut"
67
+ > &
50
68
  RefAttributes<HTMLButtonElement>
51
69
  >;
52
70
  ```
53
71
 
54
- A composer's send key. Empty, it rests disabled; given something to send it
55
- wakes with a small pop; sending, its arrow launches away and a working indicator
56
- (or, with `onStop`, a stop square) takes its place; when the work ends the arrow
57
- drops back in. All timing comes from the theme's motion tokens, and reduced
72
+ A composer's one send key, on the Caps keycap. Empty, it rests disabled; given
73
+ something to send it wakes with a small pop; sending, its arrow launches away
74
+ and a working indicator (or, with `onStop`, a stop square) takes its place in
75
+ the same key; when the work ends the arrow drops back in. It never grows a
76
+ second button. All timing comes from the theme's motion tokens, and reduced
58
77
  motion keeps short fades only.
package/docs/api/form.md CHANGED
@@ -155,6 +155,14 @@ Props accepted by FormFields.
155
155
  model: SchemaFormModel<V>;
156
156
  ```
157
157
 
158
+ ##### layout?
159
+
160
+ ```ts
161
+ optional layout?: "columns";
162
+ ```
163
+
164
+ Opt-in label/control grid for generated sections and groups.
165
+
158
166
  ##### names?
159
167
 
160
168
  ```ts
@@ -1286,6 +1294,32 @@ Receives the parsed value, never the raw input.
1286
1294
  \| `Promise`\<[`SubmitResult`](#submitresult)\<`V`\> \| `undefined`\> \|
1287
1295
  `Promise`\<`void`\> \| [`SubmitResult`](#submitresult)\<`V`\> \| `undefined`
1288
1296
 
1297
+ ##### onSaved?
1298
+
1299
+ ```ts
1300
+ optional onSaved?: (values) => void;
1301
+ ```
1302
+
1303
+ Called only after a saved result, with the new raw-value baseline.
1304
+
1305
+ ###### Parameters
1306
+
1307
+ | Parameter | Type |
1308
+ | --------- | ---- |
1309
+ | `values` | `V` |
1310
+
1311
+ ###### Returns
1312
+
1313
+ `void`
1314
+
1315
+ ##### failure?
1316
+
1317
+ ```ts
1318
+ optional failure?: string;
1319
+ ```
1320
+
1321
+ Safe message for a thrown submit; the cause is logged, never shown.
1322
+
1289
1323
  ---
1290
1324
 
1291
1325
  ### Validator
@@ -1508,14 +1542,16 @@ How a SchemaForm presents its actions and state.
1508
1542
 
1509
1543
  #### Type Declaration
1510
1544
 
1511
- | Name | Type | Description |
1512
- | --------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
1513
- | `submitLabel?` | `ReactNode` | The primary action's label; defaults to "Save changes". |
1514
- | `discardLabel?` | `ReactNode` | Show a Discard action while there are unsaved changes. |
1515
- | `allowClean?` | `boolean` | Keep the primary action available on a clean form (create forms). |
1516
- | `footer?` | `ReactNode` | Replaces the SaveBar, such as a footer with a retry key; `null` drops it. |
1517
- | `children?` | `ReactNode` | Replaces the generated field list, for custom layouts such as Screen cells: place fields with `<FormFields model names={[…]} />`. |
1518
- | `disabled?` | `boolean` | Disable every field, such as while an uncertain request is retried. |
1545
+ | Name | Type | Description |
1546
+ | --------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
1547
+ | `submitLabel?` | `ReactNode` | The primary action's label; defaults to "Save changes". |
1548
+ | `discardLabel?` | `ReactNode` | Show a Discard action while there are unsaved changes. |
1549
+ | `onCancel()?` | () => `void` | A secondary exit action, shown even when clean; never submits or discards. |
1550
+ | `cancelLabel?` | `ReactNode` | - |
1551
+ | `allowClean?` | `boolean` | Keep the primary action available on a clean form (create forms). |
1552
+ | `footer?` | `ReactNode` | Replaces the SaveBar, such as a footer with a retry key; `null` drops it. |
1553
+ | `children?` | `ReactNode` | Replaces the generated field list, for custom layouts such as Screen cells: place fields with `<FormFields model names={[…]} />`. |
1554
+ | `disabled?` | `boolean` | Disable every field, such as while an uncertain request is retried. |
1519
1555
 
1520
1556
  ---
1521
1557
 
@@ -1525,6 +1561,8 @@ How a SchemaForm presents its actions and state.
1525
1561
  type SchemaFormProps<V, Out> = SchemaFormPresentation &
1526
1562
  | {
1527
1563
  model: SchemaFormModel<V>;
1564
+ onSaved?: never;
1565
+ failure?: never;
1528
1566
  }
1529
1567
  | SchemaFormOptions<V, Out>;
1530
1568
  ```
@@ -1585,6 +1623,8 @@ Props accepted by SaveBar.
1585
1623
  | `model` | [`SchemaFormModel`](#schemaformmodel)\<`V`\> | - |
1586
1624
  | `label` | `ReactNode` | The primary action, such as "Save changes" or "Create Team". |
1587
1625
  | `discardLabel?` | `ReactNode` | Show a Discard action while there are unsaved changes. |
1626
+ | `onCancel()?` | () => `void` | A secondary exit action that never submits or resets the form. |
1627
+ | `cancelLabel?` | `ReactNode` | - |
1588
1628
  | `allowClean?` | `boolean` | Keep the primary action available on a clean form (create forms that may submit their defaults). By default a clean form has nothing to save. |
1589
1629
  | `dirtyLabel?` | `ReactNode` | The quiet readout while there are unsaved changes. |
1590
1630
  | `buttonProps?` | `Omit`\<[`ButtonProps`](components/button.md#buttonprops), `"type"` \| `"disabled"` \| `"loading"` \| `"children"` \| `"onClick"` \| `"asChild"`\> | - |
@@ -13,17 +13,19 @@
13
13
  ### formatMoney()
14
14
 
15
15
  ```ts
16
- function formatMoney(amount): string;
16
+ function formatMoney(amount, options?): string;
17
17
  ```
18
18
 
19
- Format USD with cents, preserving sub-cent precision without displaying non-zero
20
- amounts as zero.
19
+ Format USD to cents, marking non-zero sub-cent amounts rather than rounding them
20
+ to zero. Callers needing the supported five-decimal precision opt in with
21
+ `precision: "exact"`.
21
22
 
22
23
  #### Parameters
23
24
 
24
- | Parameter | Type |
25
- | --------- | -------- |
26
- | `amount` | `number` |
25
+ | Parameter | Type |
26
+ | ---------- | ------------------------------------------- |
27
+ | `amount` | `number` |
28
+ | `options?` | `Readonly`\<\{ `precision`: `"exact"`; \}\> |
27
29
 
28
30
  #### Returns
29
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/caps",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Product-neutral React primitives and blocks for Fungi interfaces",
6
6
  "type": "module",
@@ -476,7 +476,7 @@
476
476
  "devDependencies": {
477
477
  "@fungi.computer/maosaico": "0.1.0",
478
478
  "@fungi.computer/sprite": "0.2.0",
479
- "@fungi.computer/sprite-zdog": "0.2.0",
479
+ "@fungi.computer/sprite-zdog": "0.3.0",
480
480
  "@ladle/react": "5.1.1",
481
481
  "@tailwindcss/cli": "4.2.1",
482
482
  "@types/react": "18.3.3",
@@ -588,7 +588,7 @@
588
588
  "format:check": "node ../../tooling/repository/scripts/format-package-check.mjs",
589
589
  "lint:anti-slop": "node ../../tooling/repository/scripts/lint-package-anti-slop.mjs",
590
590
  "build:workbench": "node scripts/workbench-build.mjs --prepared",
591
- "test:nightly": "node --import tsx ../../tooling/repository/scripts/workbench-proofs.mjs --output ../../.botanical/caps-workbench --origin-env CAPS_WORKBENCH_URL test/screen-theme.browser.mjs test/scroll-area.browser.mjs test/display-density.browser.mjs && node test/scroll-gutter.browser.mjs",
591
+ "test:nightly": "node --import tsx ../../tooling/repository/scripts/workbench-proofs.mjs --output ../../.botanical/caps-workbench --origin-env CAPS_WORKBENCH_URL test/screen-theme.browser.mjs test/scroll-area.browser.mjs test/display-density.browser.mjs test/resize-handle.browser.mjs && node test/scroll-gutter.browser.mjs",
592
592
  "test:code-input": "node --import tsx ../../tooling/repository/scripts/workbench-proofs.mjs --output ../../.botanical/caps-code-input --origin-env CAPS_WORKBENCH_URL test/code-input.browser.mjs",
593
593
  "test:icon-choice": "node --import tsx ../../tooling/repository/scripts/workbench-proofs.mjs --output ../../.botanical/caps-icon-choice --origin-env CAPS_WORKBENCH_URL test/icon-choice.browser.mjs",
594
594
  "test:choice-label-alignment": "node --import tsx ../../tooling/repository/scripts/workbench-proofs.mjs --output ../../.botanical/caps-choice-alignment --origin-env CAPS_WORKBENCH_URL test/choice-label-alignment.browser.mjs",
@@ -134,6 +134,27 @@
134
134
  }
135
135
  }
136
136
 
137
+ /*
138
+ * A count in a bar key is a quiet readout beside its label: plain muted
139
+ * numerals below the label's cap height, never a filled pill as tall as
140
+ * the key. Every key's CountBadge draws this way.
141
+ */
142
+ .caps-app-status-bar .badge {
143
+ height: auto;
144
+ min-width: 0;
145
+ padding: 0;
146
+ border: 0;
147
+ background: none;
148
+ color: var(--stipe-fg-muted);
149
+ font-size: 0.625rem;
150
+ font-weight: 600;
151
+ line-height: 1;
152
+ }
153
+
154
+ .caps-app-status-bar .badge-warning {
155
+ color: var(--color-warning);
156
+ }
157
+
137
158
  .caps-status-bar-picker-list {
138
159
  min-width: min(18rem, calc(100vw - 1.5rem));
139
160
  }