@lotics/ui 41.3.0 → 41.4.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/MIGRATION.md CHANGED
@@ -4,6 +4,23 @@ Breaking changes, newest first — normally per major, plus the rare minor that
4
4
  anyway (recorded under its exact version). The current contract lives in `AGENTS.md` + `docs/`;
5
5
  this file exists only to move an app from one release to the next.
6
6
 
7
+ ## 41.4.0 — embedded markdown is demoted in the OUTLINE, not only on the type ladder
8
+
9
+ `<Markdown variant="embedded">` sized its headings down and left them as the tags the author
10
+ wrote, so an `h2` inside a record value still landed in heading navigation as a PEER of the
11
+ page's own sections. Measured on a record surface, the outline read:
12
+
13
+ > Activity · "Tóm tắt cuộc họp (AI) — …" · "Bài học …" · Details · Next action
14
+
15
+ — a model's call-summary title between two real sections, contributed from inside ONE row of a
16
+ feed that can hold twenty more. Nothing visual finds this, and fixing the size is what makes it
17
+ invisible: the defect stops looking wrong at the moment it stops being measurable.
18
+
19
+ Embedded headings now render `h4`/`h5`/`h6` (h1→h4, h2→h5, h3 and deeper→h6). They stay
20
+ headings, so a reader can still navigate inside a long summary; they simply can no longer reach
21
+ a section's level. **Nothing to migrate** — document-scale markdown is untouched, and the
22
+ embedded type scale is unchanged.
23
+
7
24
  ## 41.3.0 — `Timeline` rows, embedded markdown, and a disclosure that is not a link
8
25
 
9
26
  Nothing here removes an API or breaks a type. Two of the changes alter what your rows render on
package/docs/catalog.md CHANGED
@@ -413,7 +413,9 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
413
413
  field on a record, a summary in a feed row. Reach for `"embedded"` whenever the
414
414
  text was not written by whoever built the screen, which is now the common case
415
415
  because the writer is routinely a model; at document scale an `h1` it emits is
416
- the same rung as your `SectionHeadingTitle`.
416
+ the same rung as your `SectionHeadingTitle`. Embedded demotes the heading LEVEL as well as
417
+ the scale (h1→h4, h2→h5, h3+→h6): sizing alone leaves a model's `##` announcing as a PEER of
418
+ your own sections in heading navigation, which no visual check can see.
417
419
  - **`markdown_types`** — `MarkdownProps`; types only.
418
420
  - **`markdown.css`** — import once for the web markdown styling.
419
421
  - **`format_date`** — `formatDate` / `parseDate` / `toISODate` + `DateFormatStyle`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "41.3.0",
3
+ "version": "41.4.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./vite": {
@@ -8,10 +8,14 @@ import { colors } from "./colors";
8
8
  import type { MarkdownProps } from "./markdown_types";
9
9
 
10
10
  export function Markdown({ children, variant = "document" }: MarkdownProps) {
11
+ const embedded = variant === "embedded";
11
12
  return (
12
13
  <View>
13
- <div className={variant === "embedded" ? "ui-markdown ui-markdown-embedded" : "ui-markdown"}>
14
- <ReactMarkdown remarkPlugins={[remarkGfmSafe]} components={markdownComponents}>
14
+ <div className={embedded ? "ui-markdown ui-markdown-embedded" : "ui-markdown"}>
15
+ <ReactMarkdown
16
+ remarkPlugins={[remarkGfmSafe]}
17
+ components={embedded ? embeddedComponents : markdownComponents}
18
+ >
15
19
  {children}
16
20
  </ReactMarkdown>
17
21
  </div>
@@ -24,6 +28,56 @@ const markdownComponents = {
24
28
  table: CopyableTable,
25
29
  };
26
30
 
31
+ /**
32
+ * EMBEDDED MARKDOWN IS DEMOTED IN THE OUTLINE, not only on the type ladder.
33
+ *
34
+ * Sizing the headings down fixes what a screen SHOWS and nothing about what it
35
+ * ANNOUNCES: an `h2` the author of the text happened to write is still an `h2`,
36
+ * so it lands in heading navigation as a PEER of the page's own sections.
37
+ * Measured on a record surface, the outline read
38
+ *
39
+ * Activity · "Tóm tắt cuộc họp (AI) — …" · "Bài học …" · Details · Next action
40
+ *
41
+ * — a model's call-summary title sitting between two real sections, from inside
42
+ * one row of a feed that can hold twenty more. No visual probe finds this, and
43
+ * fixing the size is what makes it invisible: the defect stops looking wrong at
44
+ * the exact moment it stops being measurable.
45
+ *
46
+ * The page's sections are `h2` (`SectionHeading`), and embedded markdown is a
47
+ * VALUE inside one — inside a row, usually — so it starts at `h4`: deep enough
48
+ * that it can never be a section's peer, shallow enough to keep its own
49
+ * hierarchy. It stays a heading rather than becoming a `<div>`, because a long
50
+ * summary genuinely has parts and a reader navigating INSIDE it should still
51
+ * find them.
52
+ */
53
+ const EMBEDDED_HEADING_LEVEL: Record<string, "h4" | "h5" | "h6"> = {
54
+ h1: "h4",
55
+ h2: "h5",
56
+ h3: "h6",
57
+ h4: "h6",
58
+ h5: "h6",
59
+ h6: "h6",
60
+ };
61
+
62
+ type HeadingProps = React.ComponentPropsWithoutRef<"h4"> & { node?: unknown };
63
+
64
+ function embeddedHeading(from: keyof typeof EMBEDDED_HEADING_LEVEL) {
65
+ const Tag = EMBEDDED_HEADING_LEVEL[from];
66
+ return function EmbeddedHeading({ node: _node, children, ...rest }: HeadingProps) {
67
+ return <Tag {...rest}>{children}</Tag>;
68
+ };
69
+ }
70
+
71
+ const embeddedComponents = {
72
+ ...markdownComponents,
73
+ h1: embeddedHeading("h1"),
74
+ h2: embeddedHeading("h2"),
75
+ h3: embeddedHeading("h3"),
76
+ h4: embeddedHeading("h4"),
77
+ h5: embeddedHeading("h5"),
78
+ h6: embeddedHeading("h6"),
79
+ };
80
+
27
81
  function CopyableTable(props: React.ComponentPropsWithoutRef<"table"> & { node?: unknown }) {
28
82
  const { node: _node, children, ...rest } = props;
29
83
  const tableRef = useRef<HTMLTableElement>(null);