@lotics/ui 11.8.8 → 11.8.10

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.
@@ -74,6 +74,22 @@ review; the human presses Send. The canvas template
74
74
  ([`tpl_dieline`](../examples/tpl_dieline.tsx)) uses it this way; while running the host swaps
75
75
  `Composer` for `AgentProgress` (same pill geometry, so it reads as a morph).
76
76
 
77
+ ## The trigger — a paid action is pressed exactly once
78
+
79
+ A CTA that starts an agent run (or any paid/mutating action) must make the PRESS itself the
80
+ transition — never leave a live button waiting on a network round-trip:
81
+
82
+ - **Same-tick feedback.** On press, synchronously close the dialog / swap to the run surface
83
+ (`AgentRun` with its streaming state) so there is nothing left to click. A button that stays
84
+ pressable while an attach/start request round-trips invites double-fires — observed in
85
+ production as stacked paid runs.
86
+ - **Single-flight in app code too.** Guard the handler with a ref (state alone races the
87
+ re-render): first press wins, later presses no-op until the flow settles.
88
+ - **The SDK backstops runs, not workflows.** `useAgentRun().run()` is single-flight by default
89
+ (a duplicate call joins the in-flight run; `replace: true` is the deliberate restart) — but
90
+ `useWorkflow` calls are NOT deduped (rapid distinct calls are legitimate), so a workflow CTA
91
+ must disable itself (`loading`) while pending or a double-click writes twice.
92
+
77
93
  ## Show the work — `AgentRun`
78
94
 
79
95
  `AgentRun` (`@lotics/ui/agent_run`) is a live feed of the agent's work as a TIMELINE. The prop is
package/docs/catalog.md CHANGED
@@ -360,7 +360,7 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
360
360
  useSeparator` inserts and what menus/popovers put between option groups.
361
361
  - **`container`** — `Container`: centers content at a max width (`ContainerSize` sm|md|lg,
362
362
  `CONTAINER_SIZES`).
363
- - **`page_header`** — `PageHeader`: the page's title band.
363
+ - **`page_header`** — `PageHeader`: the page's title band; `actions` puts page-level CTAs on the title row (right-aligned), while `left`/`right` form a separate nav row above.
364
364
  - **`page_content`** — `PageContent` + `PAGE_SIZES`: the page's padded, width-capped content
365
365
  region.
366
366
  - **`accordion`** — `Accordion` + `AccordionHeader`/`AccordionTitle`/`AccordionMeta`:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "11.8.8",
3
+ "version": "11.8.10",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./tokens": "./src/tokens.ts",
@@ -7,13 +7,29 @@ interface PageHeaderProps {
7
7
  description?: string | null;
8
8
  left?: ReactNode;
9
9
  right?: ReactNode;
10
+ /** Page-level actions (CTAs), rendered on the title row, right-aligned —
11
+ * distinct from `left`/`right`, which form a separate nav row above. */
12
+ actions?: ReactNode;
10
13
  }
11
14
 
12
15
  export function PageHeader(props: PageHeaderProps) {
13
- const { title, description, left, right } = props;
16
+ const { title, description, left, right, actions } = props;
14
17
 
15
18
  const hasNav = !!left || !!right;
16
19
 
20
+ const titleRow = actions ? (
21
+ <View style={{ flexDirection: "row", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
22
+ <Text size="xxl" weight="semibold">
23
+ {title}
24
+ </Text>
25
+ {actions}
26
+ </View>
27
+ ) : (
28
+ <Text size="xxl" weight="semibold">
29
+ {title}
30
+ </Text>
31
+ );
32
+
17
33
  return (
18
34
  <View style={{ paddingBottom: 16 }}>
19
35
  {hasNav ? (
@@ -22,14 +38,10 @@ export function PageHeader(props: PageHeaderProps) {
22
38
  {left}
23
39
  {right}
24
40
  </View>
25
- <Text size="xxl" weight="semibold">
26
- {title}
27
- </Text>
41
+ {titleRow}
28
42
  </>
29
43
  ) : (
30
- <Text size="xxl" weight="semibold">
31
- {title}
32
- </Text>
44
+ titleRow
33
45
  )}
34
46
  {!!description && (
35
47
  <Text color="zinc-500">{description}</Text>