@lotics/ui 47.7.0 → 47.7.1

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/AGENTS.md CHANGED
@@ -18,7 +18,7 @@ CURRENT major only — upgrading an app across majors is `MIGRATION.md`.
18
18
  | [docs/data_entry.md](./docs/data_entry.md) | Which editing pattern for which job — inline edit, fieldset forms, choosing a CHOICE control by option count, find-or-create (`Combobox`), line items, handoffs, phased records, billing, tags, dispositions, attachments (`InlineFiles` for a record ROW, the `FilesEditor` COMPOUND for a whole section, plus the three-way file INTAKE), stage gates, and the commit-on-blur vs action-press ordering law. |
19
19
  | [docs/ai_patterns.md](./docs/ai_patterns.md) | AI acts, the human stays in charge — composer, live run feed (`AgentRun`), the one law's split — it turns on WHO supplied the values; findings, provenance, confidence (`Finding` is filed here because AI produces most of them, NOT because it is AI-only — a rules-derived contradiction is as in scope as an extraction's); **after the run** — a stored record that fills up from several writers; the whole run in a dialog, **stopping**; **review surfaces compose from atoms** — `DiffValue`, `DiffMark`, `useChangeSet`. |
20
20
  | [docs/composition.md](./docs/composition.md) | The design-language contract — **the form comes before the treatment** (name what the subject IS before reaching for parts), canvas + content column, heading altitude, register vs inset rows, the button ladder, master-detail `Drawer`, the register's rhythm, craft and TRIAGE bands, where the accent goes, theming as an app-level ESCAPE HATCH, color discipline, typography, **one fact one surface** (a chooser over the thing it chooses is a SELECTOR, not a second card grid; a control that only swaps one number for another is replaced by showing both), **a width floor answers can this be READ and never can this be operated** (the operating question passes a row whose every string is already cut), **a status column earns its slot by carrying a REASON rather than a state** (the floor asks for the fact to be visible, not for a badge), **an identity mark's GLYPH is derived from the name's distinguishing part** (an organization's first word is its legal form, so untreated every row marks one letter), **a row subject's supporting line takes a REFERENCE and never a sentence** (an explanation truncates to the clause the reader already knew), **a list stays fast by keeping IDENTITY rather than memoizing** (key by the record not the index; `memo` is the last resort and does nothing against a freshly-built prop), and where a commit sits. |
21
- | [docs/reviewing.md](./docs/reviewing.md) | **Reviewing a screen you built — and the answer when one "looks bad"** — the other docs say what good looks like; this one says how to find out whether you achieved it. Run it BEFORE the deploy: a typecheck, a lint, a test and `app check` are all blind to how a screen reads. GATES that outrank every treatment question, then the probes, each naming what to COLLECT and the SIGNATURE in the numbers — including the truncation sweep (a row whose halves both measure fine and whose text is cut) the summary-vs-facet check (a strip that keeps describing the unfiltered set) and the derivable-column test (a status badge its neighbour already answers) the first-line drift a column shows when its cell is sometimes one line and sometimes two, and the cross-FRAME check (what the screen destroys when a row arrives — the one defect that exists in no single frame). Plus the extraction snippets, what measurement cannot see, and how to act on a finding. |
21
+ | [docs/reviewing.md](./docs/reviewing.md) | **Reviewing a screen you built — and the answer when one "looks bad"** — the other docs say what good looks like; this one says how to find out whether you achieved it. Run it BEFORE the deploy: a typecheck, a lint, a test and `app check` are all blind to how a screen reads. GATES that outrank every treatment question, then the probes, each naming what to COLLECT and the SIGNATURE in the numbers — including the truncation sweep (a row whose halves both measure fine and whose text is cut) the summary-vs-facet check (a strip that keeps describing the unfiltered set) and the derivable-column test (a status badge its neighbour already answers) the first-line drift a column shows when its cell is sometimes one line and sometimes two, the cross-FRAME check (what the screen destroys when a row arrives — the one defect that exists in no single frame), and the semantic-section check (a danger or empty section drawn as an ordinary one, and the primitive nobody reached for). Plus the extraction snippets, what measurement cannot see, and how to act on a finding. |
22
22
  | [docs/testing.md](./docs/testing.md) | Driving the kit in a browser — the three anatomies where the a11y tree says one thing and a driver must do another: a `PressDoor` row whose named button always intercepts pointer events (by design), portalled overlays that render at the top of the DOM, and custom pointer drag that `dragTo` cannot move. |
23
23
  | [docs/templates.md](./docs/templates.md) | The map of `examples/tpl_*.tsx` — what shape each template solves and which to start from (copy + adapt, never import) — plus the record-surface composition rules (pipeline order, static shape, decision budget) and the ACTIVITY shape — a communications feed where the row's label is the GIST and the body varies by medium, one anatomy rather than a row type per kind. |
24
24
 
package/docs/catalog.md CHANGED
@@ -1675,7 +1675,8 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
1675
1675
  **The row OWNS its `title`/`description` pair, so do not hand-roll one beside it.** Both
1676
1676
  lines are `sm`, `leading="tight"`, no gap between them — the stacked-pair law in
1677
1677
  [composition.md](./composition.md), applied by the component rather than restated at each
1678
- call site. The row is 56.
1678
+ call site. The row's floor is 64 — 12 a side around a tight pair and an `md` leading mark —
1679
+ and it grows for a `description` node taller than a line.
1679
1680
  A `description` passed as a NODE keeps its own leading — the component only sets it on the
1680
1681
  string branch, because forcing a line box on somebody else's subtree re-spaces content that
1681
1682
  may not be a line of text at all.
@@ -1751,6 +1751,22 @@ The hit area is unchanged — it paints outside the box rather than growing it
1751
1751
  where it would if the control were not there. The same applies to any verb riding a value: a
1752
1752
  `CopyButton`, an `InlineButton`, a count chip.
1753
1753
 
1754
+ ## "Who can reach this" is drawn as MARKS, on every surface that asks it
1755
+
1756
+ Access is a set of people, so it renders as `AvatarGroup` — a register cell, a settings row, a
1757
+ detail header. A word in place of the marks (`Shared`, `2 people`) answers a smaller question than
1758
+ the faces do: it says THAT the thing is shared without saying WITH WHOM, which is the half the
1759
+ reader opened the screen for, and it puts one datum in two treatments the moment any other surface
1760
+ draws the marks.
1761
+
1762
+ The one case marks cannot carry is the whole organization: "everyone" is not a set you can show
1763
+ three of, and the three that happen to sort first would say something false about who else is in
1764
+ there. That gets an icon and a word.
1765
+
1766
+ Whether the OWNER is among the marks is the caller's decision, not a flag on the component — a
1767
+ register with its own owner column passes the shares alone, a list without one leads with the
1768
+ owner. Compose it; a boolean would make one component answer two questions.
1769
+
1754
1770
  ## Identity marks — a mark that is the same on every row carries nothing
1755
1771
 
1756
1772
  `Avatar` derives its ground from the NAME (`avatar_tone`), and takes `shape="circle" | "square"`
package/docs/reviewing.md CHANGED
@@ -232,6 +232,13 @@ reads as one cluster. One `gap` on a container makes every child a peer; wrap th
232
232
  space the groups. Worst where both items are small grey glyphs on one line, because nothing but
233
233
  spacing distinguishes them.
234
234
 
235
+ **The inverted case is easiest to measure and easiest to miss: a control sitting directly on a
236
+ list.** An action above a `List` with nothing between them measures 0, against the 1px divider
237
+ INSIDE the list — so the boundary between two different things is smaller than the boundary
238
+ between two rows of one thing, and the button reads as the list's first row. Measure the CTA's
239
+ bottom against the first row's top on any panel built as *action, then list*; it is a per-screen
240
+ defect, so finding it on one panel is a reason to sweep the siblings.
241
+
235
242
  → [composition.md](./composition.md) §"Touch & whitespace".
236
243
 
237
244
  ### 5. State diff
@@ -675,6 +682,27 @@ its whole subtree on each background refresh.
675
682
 
676
683
  → [composition.md](./composition.md) §"A list stays fast by keeping IDENTITY, not by memoizing".
677
684
 
685
+ ### 9d. A SEMANTIC section drawn as an ordinary one — and the primitive nobody reached for
686
+ Probes 1 and 2 group by shape and find each group internally consistent. The defect neither sees
687
+ is a section whose MEANING is special — destructive, warning, empty, generated — rendered in the
688
+ same heading treatment, on the same ground, as the ordinary sections beside it. Every measurement
689
+ passes, and the meaning is carried by the heading's WORDS alone: delete the word "Danger" and
690
+ nothing on screen still says it.
691
+
692
+ **Collect**, per section on the surface: heading `rung / weight / ink`, the section's own
693
+ background and border, and the treatment of the action inside it. **The signature** is a
694
+ semantically-marked section byte-identical to its neighbours — and, downstream of that, its
695
+ action reading as the FAINTEST control on a screen whose ordinary Save is solid (probe 2's
696
+ singleton ground, arrived at from the other end).
697
+
698
+ **The cheaper detection is structural: grep the kit for a component named after the meaning
699
+ before composing one.** A section the kit already ships — `DangerZone`, `EmptyState` — hand-rolled
700
+ out of a divider, a heading and a button loses more than its treatment: it loses the SLOTS the
701
+ primitive requires, which is why hand-rolled destructive sections routinely carry no consequence
702
+ line at all. If a screen's section has a name, that name is the thing to search for. And a
703
+ component that exists and is used NOWHERE is the same finding seen from the kit's side — worth a
704
+ sweep, not a second copy.
705
+
678
706
  ### 10. Microcopy
679
707
  - **No punctuation doing a word's job.** ` · ` is banned outright — it claims a relationship while
680
708
  refusing to name it, and it is the loudest templated-metadata tell there is. If a mark cannot be
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "47.7.0",
3
+ "version": "47.7.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./accordion": "./src/accordion.tsx",
package/src/list_item.tsx CHANGED
@@ -171,18 +171,17 @@ function PressRow(props: {
171
171
  }
172
172
 
173
173
  const styles = StyleSheet.create({
174
- // 56, not 64. The height was sized around a pair rendered as prosetwo 24px
175
- // boxes plus a 2px gap is 50, and 64 left 14px of slack the row did not ask
176
- // for, which is what made a correctly-sized `md` mark (28px, the documented
177
- // rung for a person in a list) look adrift in it. A tight pair is 40, so 56
178
- // gives it the same 8-a-side the rest of the kit's rows carry. A title-only
179
- // row keeps one 20px line centred in the same box, so the two shapes still
180
- // share a beat.
174
+ // 64 around a tight pair of 40 and an `md` (28px) leading mark12 a side,
175
+ // which is what lets a settings list of identity rows read as a list rather
176
+ // than a table. A title-only row keeps one 20px line centred in the same box,
177
+ // so the two shapes share a beat. `minHeight`, not `height`: a `description`
178
+ // node the caller supplies (a chip row, two stacked facts) is taller than a
179
+ // line of text, and a fixed height clips it.
181
180
  container: {
182
181
  flexDirection: "row",
183
182
  alignItems: "center",
184
183
  gap: 8,
185
- height: 56,
184
+ minHeight: 64,
186
185
  paddingHorizontal: 8,
187
186
  marginHorizontal: -8,
188
187
  borderRadius: 12,