@cueplusplus/ui 0.21.0 → 0.23.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/CHANGELOG.md +67 -0
- package/README.md +1 -1
- package/dist/elements/markdown.d.ts +29 -9
- package/dist/elements/markdown.js +102 -13
- package/dist/forms/_chassis.d.ts +9 -1
- package/dist/forms/_chassis.js +11 -3
- package/dist/overlays/_surface.d.ts +1 -1
- package/dist/styles.css +69 -0
- package/dist/system/_paint-stroke-sunken.js +22 -2
- package/dist/system/_paint-stroke-surface-1.js +5 -1
- package/dist/system/_paint-stroke-surface-2.js +5 -1
- package/dist/system/_style-disabled.js +1 -7
- package/dist/theming/contrast.d.ts +2 -2
- package/dist/theming/contrast.js +2 -2
- package/dist/theming/create-theme.js +15 -6
- package/dist/theming/index.d.ts +2 -2
- package/dist/theming/index.js +2 -2
- package/dist/theming/serialize.js +2 -1
- package/manifest/components/alert-dialog.json +1 -0
- package/manifest/components/autocomplete.json +2 -0
- package/manifest/components/button.json +0 -3
- package/manifest/components/card.json +1 -0
- package/manifest/components/channel-matrix.json +1 -0
- package/manifest/components/chart-tooltip-content.json +1 -0
- package/manifest/components/checkbox-group.json +0 -3
- package/manifest/components/checkbox.json +0 -3
- package/manifest/components/color-field.json +1 -0
- package/manifest/components/color-picker.json +1 -0
- package/manifest/components/combobox.json +2 -0
- package/manifest/components/command-palette.json +1 -0
- package/manifest/components/composer.json +1 -0
- package/manifest/components/context-menu.json +1 -0
- package/manifest/components/date-field.json +1 -0
- package/manifest/components/date-picker.json +1 -0
- package/manifest/components/date-range-picker.json +1 -0
- package/manifest/components/dialog.json +1 -0
- package/manifest/components/dmx-bar.json +1 -0
- package/manifest/components/dmx-strip.json +1 -0
- package/manifest/components/drawer.json +1 -0
- package/manifest/components/dropdown-menu.json +1 -0
- package/manifest/components/env-var-input.json +1 -0
- package/manifest/components/hover-card.json +1 -0
- package/manifest/components/input-group.json +1 -0
- package/manifest/components/input.json +1 -0
- package/manifest/components/markdown-text.json +16 -2
- package/manifest/components/menubar.json +1 -0
- package/manifest/components/multi-select.json +2 -0
- package/manifest/components/musical-time-input.json +1 -0
- package/manifest/components/navigation-menu.json +1 -0
- package/manifest/components/number-field.json +1 -0
- package/manifest/components/otp-field.json +1 -0
- package/manifest/components/password-input.json +1 -0
- package/manifest/components/popover.json +1 -0
- package/manifest/components/scrub-input.json +1 -0
- package/manifest/components/search-input.json +1 -0
- package/manifest/components/select.json +2 -0
- package/manifest/components/sheet.json +1 -0
- package/manifest/components/switch.json +0 -3
- package/manifest/components/tags-input.json +2 -0
- package/manifest/components/textarea.json +1 -0
- package/manifest/components/theme-configurator.json +1 -0
- package/manifest/components/time-field.json +1 -0
- package/manifest/components/toast.json +1 -0
- package/manifest/components/toggle.json +0 -3
- package/manifest/components/tooltip.json +1 -0
- package/manifest/manifest.json +49 -49
- package/manifest/tokens.json +5 -1
- package/package.json +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# @cueplusplus/ui
|
|
2
2
|
|
|
3
|
+
## 0.23.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dae9dec: **A field's edge can reach 3:1: `data-field-edge="contrast"`** (cue-ui-60). Every shipped theme's
|
|
8
|
+
`--cue-border` measured 1.07:1 to 2.02:1 against `surface-1`, under WCAG 1.4.11's 3:1 for the line
|
|
9
|
+
that marks out where to type. The border stays as it is, because it is a box edge and a quiet one on
|
|
10
|
+
purpose, and a field's edge becomes a role of its own.
|
|
11
|
+
|
|
12
|
+
- **`--cue-field-edge`** is each block's border moved toward its ink until it clears 3:1 on its
|
|
13
|
+
`surface-1`, written per block as a literal. Every theme package, `theme-base`'s own palette and
|
|
14
|
+
`createTheme()` write it, and the contrast report holds it to 3:1 as the required pair
|
|
15
|
+
`field-edge/surface-1`.
|
|
16
|
+
- **Opt in with `data-field-edge="contrast"`** on any element, `<html>` included. Every recessed
|
|
17
|
+
control inside then draws its resting edge in `--cue-field-edge`, disabled ones too, and over a
|
|
18
|
+
paint pack's stroke. Hover, focus and invalid edges keep their own colours. Outside the attribute,
|
|
19
|
+
nothing changes.
|
|
20
|
+
- **`edgeToContrast(edge, ground, toward, minimum = 3)`** is the arithmetic, exported from
|
|
21
|
+
`@cueplusplus/theme-base/contrast` and `@cueplusplus/ui/theming`.
|
|
22
|
+
- `@cueplusplus/tokens` publishes `field-edge` as a derived name, with `var(--cue-border)` as its
|
|
23
|
+
template. A stylesheet built before this release falls back to the plain border.
|
|
24
|
+
|
|
25
|
+
**For a theme package of your own:** `theme-tools` now writes one more line per block. Run
|
|
26
|
+
`generate` once after upgrading; until then `check` reports the stylesheet out of date.
|
|
27
|
+
|
|
28
|
+
- 39884b3: **`MarkdownText` renders GitHub tables** (cue-ui-58). An agent's table used to arrive as one
|
|
29
|
+
paragraph of pipes: the parser read CommonMark only, and the sanitizer allowed no table elements.
|
|
30
|
+
Tables are now the one part of GFM the parser reads. Each column is aligned as its delimiter row
|
|
31
|
+
says, header cells sit on `surface-2`, and the table scrolls sideways inside a named,
|
|
32
|
+
keyboard-reachable region rather than widening the turn. Task lists, footnotes and autolink literals
|
|
33
|
+
stay out, and raw HTML is still refused: a cell's markup is text, and alignment is the only style a
|
|
34
|
+
cell carries.
|
|
35
|
+
|
|
36
|
+
**`remarkPlugins`** adds parser plugins after the table syntax. It is a seam for the parser, not
|
|
37
|
+
for the page: whatever a plugin puts in the tree passes the same sanitizer, so it can change what
|
|
38
|
+
the markdown means but cannot widen what renders.
|
|
39
|
+
|
|
40
|
+
### Migrating
|
|
41
|
+
- **`@cueplusplus/ui/elements/markdown` has two more optional peers**, the table half of GFM:
|
|
42
|
+
`pnpm add micromark-extension-gfm-table mdast-util-gfm-table`. Like `react-markdown` and
|
|
43
|
+
`rehype-sanitize`, they are needed only where this subpath is imported. A build that imports it
|
|
44
|
+
without them fails to resolve the module, and names it.
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [dae9dec]
|
|
49
|
+
- @cueplusplus/tokens@0.23.0
|
|
50
|
+
- @cueplusplus/theme-base@1.4.0
|
|
51
|
+
|
|
52
|
+
## 0.22.0
|
|
53
|
+
|
|
54
|
+
### Minor Changes
|
|
55
|
+
|
|
56
|
+
- bdc8a32: A field's fill is a name now: `--cue-style-field-fill`. Every recessed control — Input, NumberField, SearchInput, Select, Combobox, Autocomplete, MultiSelect, TagsInput, Textarea, OtpField, ScrubInput, InputGroup, ColorField, ColorPicker, the date fields and MusicalTimeInput — fills with it before `--cue-sunken`, and a disabled one follows it unless `--cue-style-disabled-fill` is set. Nothing sets it by default, so nothing changes until a style set or a subtree does: `style={{ "--cue-style-field-fill": "var(--cue-surface-2)" }}` on any wrapper fills every field inside it. In the manifest, Button, Checkbox, CheckboxGroup, Switch and Toggle no longer list three stroke tokens they never painted.
|
|
57
|
+
|
|
58
|
+
The studio's generated tables carry the new catalogue identity, so a scene validates against the vocabulary that now includes a field's fill.
|
|
59
|
+
|
|
60
|
+
- 1962b97: Fields can take their fill from the ground they stand on. Put `data-field-ground="relative"` on any element — `<html>` included — and every recessed control inside it fills one step off its nearest container, toward its text colour: lighter on a dark card, deeper on a light one. `--cue-style-field-step` sets the distance (6% by default) and `--cue-style-field-toward` the direction (`black` inline is always deeper). Containers painted with `bg-bg`, `bg-sunken`, `bg-surface-1/2/3`, a Card or an overlay say what they painted as `--cue-ground` (an overlay's is its `--cue-style-overlay-ground` when a Look sets one, so its fields follow the overlay the Look drew); anything painted another way sets `--cue-ground` itself. `--cue-style-field-fill` still wins, a disabled field follows, and nothing changes outside the attribute. The derivation is a `field-ground` utility in `@cueplusplus/ui/styles.css`, so a field of your own can wear it too.
|
|
61
|
+
|
|
62
|
+
The studio's generated tables carry the new catalogue identity, so a scene validates against the vocabulary that now includes the relative fill's two names.
|
|
63
|
+
|
|
64
|
+
### Patch Changes
|
|
65
|
+
|
|
66
|
+
- Updated dependencies [bdc8a32]
|
|
67
|
+
- Updated dependencies [1962b97]
|
|
68
|
+
- @cueplusplus/tokens@0.22.0
|
|
69
|
+
|
|
3
70
|
## 0.21.0
|
|
4
71
|
|
|
5
72
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -105,7 +105,7 @@ their own, so importing a button never pulls a charting library into your bundle
|
|
|
105
105
|
| `@cueplusplus/ui/chat` | the multi-agent transcript: messages, composer, HITL questions, agent colour |
|
|
106
106
|
| `@cueplusplus/ui/agent-runtime` | the streaming layer under Chat; needs `@assistant-ui/react` |
|
|
107
107
|
| `@cueplusplus/ui/elements` | the agent's own surface, vendored from assistant-ui and repainted onto the tokens; needs `lucide-react` and `heat-graph` (+ `./elements.css`) |
|
|
108
|
-
| `@cueplusplus/ui/elements/markdown` | `MarkdownText`; needs `react-markdown`, `rehype-sanitize` |
|
|
108
|
+
| `@cueplusplus/ui/elements/markdown` | `MarkdownText`; needs `react-markdown`, `rehype-sanitize`, `micromark-extension-gfm-table`, `mdast-util-gfm-table` |
|
|
109
109
|
| `@cueplusplus/ui/elements/generative` | the generative-UI renderer and its spec (+ `./elements/generative.css`) |
|
|
110
110
|
| `@cueplusplus/ui/elements/replay` | the scripted session-replay frame |
|
|
111
111
|
| `@cueplusplus/ui/dmx` | 512 bytes at wire rate: the bar, the strip, the patch bar, the channel grid |
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import Markdown from "react-markdown";
|
|
2
3
|
//#region src/elements/markdown.d.ts
|
|
3
4
|
/** A fenced block, handed to a highlighter that was opted into. */
|
|
4
5
|
interface MarkdownCodeBlock {
|
|
@@ -50,6 +51,16 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
|
|
|
50
51
|
* behaviour they inherited.
|
|
51
52
|
*/
|
|
52
53
|
renderCodeBlock?: (block: MarkdownCodeBlock) => React.ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* More remark plugins, run after the component's own table syntax.
|
|
56
|
+
*
|
|
57
|
+
* A seam for the parser, not for the page: whatever a plugin adds to the
|
|
58
|
+
* tree still passes the same sanitizer, so a plugin can change what the
|
|
59
|
+
* markdown *means* — a mention syntax, a glossary link — but cannot widen
|
|
60
|
+
* what renders. An element outside the subset is dropped, and an attribute
|
|
61
|
+
* the schema does not name never arrives.
|
|
62
|
+
*/
|
|
63
|
+
remarkPlugins?: React.ComponentProps<typeof Markdown>["remarkPlugins"];
|
|
53
64
|
}
|
|
54
65
|
/**
|
|
55
66
|
* Model output, rendered.
|
|
@@ -84,9 +95,11 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
|
|
|
84
95
|
* - **Attributes are enumerated, not filtered.** An element that is not in the
|
|
85
96
|
* schema renders nothing; an element that is renders only the attributes
|
|
86
97
|
* named beside it — `href`/`title` on a link, `src`/`alt`/`title` on an
|
|
87
|
-
* image, `language-…` on a `code
|
|
98
|
+
* image, `language-…` on a `code`, and `align` on a table cell, held to
|
|
99
|
+
* `left`, `center` or `right`. No element in the subset accepts `id`,
|
|
88
100
|
* `style`, `srcset`, `formaction` or any `on*` handler, so none of them has
|
|
89
|
-
* an attribute to arrive on
|
|
101
|
+
* an attribute to arrive on; the one inline style anything carries is a
|
|
102
|
+
* cell's `text-align`, which the renderer writes from that `align`.
|
|
90
103
|
* - **URLs are allow-listed twice.** `http`, `https` and `mailto` on both
|
|
91
104
|
* `href` and `src`; a relative URL stays relative; everything else — a
|
|
92
105
|
* `javascript:` link, a `data:text/html` image, `vbscript:`, `blob:`,
|
|
@@ -98,10 +111,14 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
|
|
|
98
111
|
*
|
|
99
112
|
* **The subset.** Paragraphs, ATX and setext headings, bullet and ordered
|
|
100
113
|
* lists, block quotes, thematic breaks, fenced and indented code, inline code,
|
|
101
|
-
* emphasis, strong, strikethrough, links, images,
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
114
|
+
* emphasis, strong, strikethrough, links, images, hard breaks, and GitHub
|
|
115
|
+
* tables. Tables are the one part of GFM the parser reads, each column aligned
|
|
116
|
+
* as its delimiter row says and wrapped in a named region that scrolls
|
|
117
|
+
* sideways; task lists, footnotes and autolink literals stay out — an explicit
|
|
118
|
+
* `<https://…>` autolink still works. Fenced blocks are plain text unless
|
|
119
|
+
* {@link MarkdownTextProps.renderCodeBlock} is given, and
|
|
120
|
+
* {@link MarkdownTextProps.remarkPlugins} adds parser plugins without widening
|
|
121
|
+
* the subset.
|
|
105
122
|
*
|
|
106
123
|
* **Streaming.** Pass `streaming` while the turn is arriving and the token the
|
|
107
124
|
* model is halfway through typing is hidden rather than rendered as
|
|
@@ -110,10 +127,11 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
|
|
|
110
127
|
* words appear as they arrive; only their formatting waits for the closing
|
|
111
128
|
* token.
|
|
112
129
|
*
|
|
113
|
-
* `react-markdown
|
|
114
|
-
*
|
|
130
|
+
* `react-markdown`, `rehype-sanitize` and GFM's table pair
|
|
131
|
+
* (`micromark-extension-gfm-table`, `mdast-util-gfm-table`) are optional peers
|
|
132
|
+
* of this subpath alone, which is why this component is not re-exported from
|
|
115
133
|
* `@cueplusplus/ui/elements`: a console that renders a transcript without
|
|
116
|
-
* markdown should install
|
|
134
|
+
* markdown should install none of them.
|
|
117
135
|
*
|
|
118
136
|
* @example
|
|
119
137
|
* <Message agent="researcher">
|
|
@@ -125,6 +143,8 @@ interface MarkdownTextProps extends Omit<React.ComponentPropsWithoutRef<"div">,
|
|
|
125
143
|
* <MarkdownText renderCodeBlock={({ language, code }) => <Highlight lang={language} code={code} />}>
|
|
126
144
|
* {answer}
|
|
127
145
|
* </MarkdownText>
|
|
146
|
+
* @example
|
|
147
|
+
* <MarkdownText remarkPlugins={[remarkMentions]}>{turn.text}</MarkdownText>
|
|
128
148
|
*/
|
|
129
149
|
declare const MarkdownText: React.ForwardRefExoticComponent<MarkdownTextProps & React.RefAttributes<HTMLDivElement>>;
|
|
130
150
|
//#endregion
|
|
@@ -4,6 +4,8 @@ import { FOCUS_RING } from "../system/_focus-ring.js";
|
|
|
4
4
|
import { codeScroll, codeSurface, field, mono } from "./surfaces.js";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { gfmTableFromMarkdown } from "mdast-util-gfm-table";
|
|
8
|
+
import { gfmTable } from "micromark-extension-gfm-table";
|
|
7
9
|
import Markdown from "react-markdown";
|
|
8
10
|
import rehypeSanitize from "rehype-sanitize";
|
|
9
11
|
//#region src/elements/markdown.tsx
|
|
@@ -19,9 +21,13 @@ import rehypeSanitize from "rehype-sanitize";
|
|
|
19
21
|
* `MarkdownText`'s JSDoc quotes it, and a test asserts nothing outside it
|
|
20
22
|
* survives.
|
|
21
23
|
*
|
|
24
|
+
* The six table elements are here because GFM's table syntax is the one part
|
|
25
|
+
* of GFM the parser reads (see {@link remarkTables}): a table is inert, and it
|
|
26
|
+
* is how an agent presents a comparison.
|
|
27
|
+
*
|
|
22
28
|
* Deliberately absent, and each for a reason: `input` (GFM task lists would
|
|
23
|
-
* put a control inside a transcript), `
|
|
24
|
-
*
|
|
29
|
+
* put a control inside a transcript), `sup`/`section` (footnotes, which a
|
|
30
|
+
* reply does not need), `svg`/`math` (an untrusted
|
|
25
31
|
* vector is a script host in several browsers' history), `details`/`summary`
|
|
26
32
|
* (disclosure is a component, not a token stream), and every sectioning or
|
|
27
33
|
* embedding tag — `iframe`, `object`, `embed`, `script`, `style`, `form`.
|
|
@@ -46,6 +52,12 @@ const ALLOWED_TAGS = [
|
|
|
46
52
|
"p",
|
|
47
53
|
"pre",
|
|
48
54
|
"strong",
|
|
55
|
+
"table",
|
|
56
|
+
"tbody",
|
|
57
|
+
"td",
|
|
58
|
+
"th",
|
|
59
|
+
"thead",
|
|
60
|
+
"tr",
|
|
49
61
|
"ul"
|
|
50
62
|
];
|
|
51
63
|
/**
|
|
@@ -72,6 +84,9 @@ const ALLOWED_PROTOCOLS = [
|
|
|
72
84
|
* handler unreachable by construction instead of by deny-list. `code` may
|
|
73
85
|
* carry a `className`, and only one matching `language-…`, because that is how
|
|
74
86
|
* a fenced block's info string reaches {@link MarkdownTextProps.renderCodeBlock}.
|
|
87
|
+
* A table cell may carry `align`, and only one of the three values the
|
|
88
|
+
* delimiter row can write; the renderer turns it into `text-align`, which is
|
|
89
|
+
* the one inline style anything in this surface ever carries.
|
|
75
90
|
*/
|
|
76
91
|
const SCHEMA = {
|
|
77
92
|
tagNames: [...ALLOWED_TAGS],
|
|
@@ -84,6 +99,18 @@ const SCHEMA = {
|
|
|
84
99
|
],
|
|
85
100
|
code: [["className", /^language-[\w+#.-]+$/]],
|
|
86
101
|
ol: ["start"],
|
|
102
|
+
th: [[
|
|
103
|
+
"align",
|
|
104
|
+
"left",
|
|
105
|
+
"center",
|
|
106
|
+
"right"
|
|
107
|
+
]],
|
|
108
|
+
td: [[
|
|
109
|
+
"align",
|
|
110
|
+
"left",
|
|
111
|
+
"center",
|
|
112
|
+
"right"
|
|
113
|
+
]],
|
|
87
114
|
"*": []
|
|
88
115
|
},
|
|
89
116
|
protocols: {
|
|
@@ -304,6 +331,20 @@ function sealStream(source) {
|
|
|
304
331
|
for (let index = 0; index < tail.length; index += 1) if (!erase.has(index)) sealed += tail[index];
|
|
305
332
|
return head + sealed;
|
|
306
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* GitHub's table syntax, as a remark plugin, and nothing else from GFM.
|
|
336
|
+
*
|
|
337
|
+
* `remark-gfm` would bring task lists (a checkbox in a transcript), footnotes
|
|
338
|
+
* and autolink literals along with tables. This registers the table tokenizer
|
|
339
|
+
* and its tree handlers alone, which is what `remark-gfm` itself does for each
|
|
340
|
+
* of its five extensions, one at a time. The sanitizer still decides what a
|
|
341
|
+
* table may render as: the six elements and a cell's `align`.
|
|
342
|
+
*/
|
|
343
|
+
function remarkTables() {
|
|
344
|
+
const data = this.data();
|
|
345
|
+
(data.micromarkExtensions ??= []).push(gfmTable());
|
|
346
|
+
(data.fromMarkdownExtensions ??= []).push(gfmTableFromMarkdown());
|
|
347
|
+
}
|
|
307
348
|
/** The text and info string of a fenced block, read off its hast node. */
|
|
308
349
|
function readCodeBlock(node) {
|
|
309
350
|
const code = node?.children?.find((child) => child.tagName === "code");
|
|
@@ -347,9 +388,11 @@ function readCodeBlock(node) {
|
|
|
347
388
|
* - **Attributes are enumerated, not filtered.** An element that is not in the
|
|
348
389
|
* schema renders nothing; an element that is renders only the attributes
|
|
349
390
|
* named beside it — `href`/`title` on a link, `src`/`alt`/`title` on an
|
|
350
|
-
* image, `language-…` on a `code
|
|
391
|
+
* image, `language-…` on a `code`, and `align` on a table cell, held to
|
|
392
|
+
* `left`, `center` or `right`. No element in the subset accepts `id`,
|
|
351
393
|
* `style`, `srcset`, `formaction` or any `on*` handler, so none of them has
|
|
352
|
-
* an attribute to arrive on
|
|
394
|
+
* an attribute to arrive on; the one inline style anything carries is a
|
|
395
|
+
* cell's `text-align`, which the renderer writes from that `align`.
|
|
353
396
|
* - **URLs are allow-listed twice.** `http`, `https` and `mailto` on both
|
|
354
397
|
* `href` and `src`; a relative URL stays relative; everything else — a
|
|
355
398
|
* `javascript:` link, a `data:text/html` image, `vbscript:`, `blob:`,
|
|
@@ -361,10 +404,14 @@ function readCodeBlock(node) {
|
|
|
361
404
|
*
|
|
362
405
|
* **The subset.** Paragraphs, ATX and setext headings, bullet and ordered
|
|
363
406
|
* lists, block quotes, thematic breaks, fenced and indented code, inline code,
|
|
364
|
-
* emphasis, strong, strikethrough, links, images,
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
407
|
+
* emphasis, strong, strikethrough, links, images, hard breaks, and GitHub
|
|
408
|
+
* tables. Tables are the one part of GFM the parser reads, each column aligned
|
|
409
|
+
* as its delimiter row says and wrapped in a named region that scrolls
|
|
410
|
+
* sideways; task lists, footnotes and autolink literals stay out — an explicit
|
|
411
|
+
* `<https://…>` autolink still works. Fenced blocks are plain text unless
|
|
412
|
+
* {@link MarkdownTextProps.renderCodeBlock} is given, and
|
|
413
|
+
* {@link MarkdownTextProps.remarkPlugins} adds parser plugins without widening
|
|
414
|
+
* the subset.
|
|
368
415
|
*
|
|
369
416
|
* **Streaming.** Pass `streaming` while the turn is arriving and the token the
|
|
370
417
|
* model is halfway through typing is hidden rather than rendered as
|
|
@@ -373,10 +420,11 @@ function readCodeBlock(node) {
|
|
|
373
420
|
* words appear as they arrive; only their formatting waits for the closing
|
|
374
421
|
* token.
|
|
375
422
|
*
|
|
376
|
-
* `react-markdown
|
|
377
|
-
*
|
|
423
|
+
* `react-markdown`, `rehype-sanitize` and GFM's table pair
|
|
424
|
+
* (`micromark-extension-gfm-table`, `mdast-util-gfm-table`) are optional peers
|
|
425
|
+
* of this subpath alone, which is why this component is not re-exported from
|
|
378
426
|
* `@cueplusplus/ui/elements`: a console that renders a transcript without
|
|
379
|
-
* markdown should install
|
|
427
|
+
* markdown should install none of them.
|
|
380
428
|
*
|
|
381
429
|
* @example
|
|
382
430
|
* <Message agent="researcher">
|
|
@@ -388,8 +436,10 @@ function readCodeBlock(node) {
|
|
|
388
436
|
* <MarkdownText renderCodeBlock={({ language, code }) => <Highlight lang={language} code={code} />}>
|
|
389
437
|
* {answer}
|
|
390
438
|
* </MarkdownText>
|
|
439
|
+
* @example
|
|
440
|
+
* <MarkdownText remarkPlugins={[remarkMentions]}>{turn.text}</MarkdownText>
|
|
391
441
|
*/
|
|
392
|
-
const MarkdownText = React.forwardRef(function MarkdownText({ className, children, streaming = false, target = "_blank", headingLevel = 2, renderCodeBlock, ...elementProps }, ref) {
|
|
442
|
+
const MarkdownText = React.forwardRef(function MarkdownText({ className, children, streaming = false, target = "_blank", headingLevel = 2, renderCodeBlock, remarkPlugins, ...elementProps }, ref) {
|
|
393
443
|
const source = streaming ? sealStream(children) : children;
|
|
394
444
|
const components = React.useMemo(() => markdownComponents({
|
|
395
445
|
target,
|
|
@@ -408,6 +458,7 @@ const MarkdownText = React.forwardRef(function MarkdownText({ className, childre
|
|
|
408
458
|
...elementProps,
|
|
409
459
|
children: /* @__PURE__ */ jsx(Markdown, {
|
|
410
460
|
components,
|
|
461
|
+
remarkPlugins: [remarkTables, ...remarkPlugins ?? []],
|
|
411
462
|
rehypePlugins: [[rehypeSanitize, SCHEMA]],
|
|
412
463
|
skipHtml: true,
|
|
413
464
|
urlTransform: safeUrl,
|
|
@@ -522,7 +573,45 @@ function markdownComponents(options) {
|
|
|
522
573
|
children: renderCodeBlock(block)
|
|
523
574
|
})
|
|
524
575
|
});
|
|
525
|
-
}
|
|
576
|
+
},
|
|
577
|
+
table: ({ children }) => /* @__PURE__ */ jsx("div", {
|
|
578
|
+
role: "region",
|
|
579
|
+
"aria-label": "Table",
|
|
580
|
+
tabIndex: 0,
|
|
581
|
+
"data-slot": "markdown-table-scroll",
|
|
582
|
+
className: cn("rounded-control border border-border outline-none", codeScroll, FOCUS_RING),
|
|
583
|
+
children: /* @__PURE__ */ jsx("table", {
|
|
584
|
+
"data-slot": "markdown-table",
|
|
585
|
+
className: "w-full border-collapse text-(length:--cue-text-ui)",
|
|
586
|
+
children
|
|
587
|
+
})
|
|
588
|
+
}),
|
|
589
|
+
thead: ({ children }) => /* @__PURE__ */ jsx("thead", {
|
|
590
|
+
"data-slot": "markdown-table-head",
|
|
591
|
+
className: "bg-surface-2",
|
|
592
|
+
children
|
|
593
|
+
}),
|
|
594
|
+
tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", {
|
|
595
|
+
"data-slot": "markdown-table-body",
|
|
596
|
+
children
|
|
597
|
+
}),
|
|
598
|
+
tr: ({ children }) => /* @__PURE__ */ jsx("tr", {
|
|
599
|
+
"data-slot": "markdown-table-row",
|
|
600
|
+
className: "border-b border-border last:border-b-0",
|
|
601
|
+
children
|
|
602
|
+
}),
|
|
603
|
+
th: ({ children, style }) => /* @__PURE__ */ jsx("th", {
|
|
604
|
+
"data-slot": "markdown-table-header",
|
|
605
|
+
style,
|
|
606
|
+
className: "px-(--cue-space-3) py-(--cue-space-2) text-start align-bottom font-medium text-fg",
|
|
607
|
+
children
|
|
608
|
+
}),
|
|
609
|
+
td: ({ children, style }) => /* @__PURE__ */ jsx("td", {
|
|
610
|
+
"data-slot": "markdown-table-cell",
|
|
611
|
+
style,
|
|
612
|
+
className: "px-(--cue-space-3) py-(--cue-space-2) align-top text-fg",
|
|
613
|
+
children
|
|
614
|
+
})
|
|
526
615
|
};
|
|
527
616
|
}
|
|
528
617
|
//#endregion
|
package/dist/forms/_chassis.d.ts
CHANGED
|
@@ -102,6 +102,14 @@ import { FormControlSize } from "./_chassis-size.js";
|
|
|
102
102
|
* set (a hex colour code, a DMX value) restates `font-mono` on top, as
|
|
103
103
|
* `color-picker.tsx`'s hex field does.
|
|
104
104
|
*
|
|
105
|
+
* **`field-ground`** is `styles.css`'s opt-in relative fill (spec
|
|
106
|
+
* 2026-10-08-field-ground R3): inside `[data-field-ground="relative"]` it
|
|
107
|
+
* re-declares this field's own `--cue-sunken` one step off the ground it stands
|
|
108
|
+
* on; anywhere else it compiles to nothing that applies. The same hook carries
|
|
109
|
+
* the contrast edge (cue-ui-60): inside `[data-field-edge="contrast"]` this
|
|
110
|
+
* field's resting stroke reads `--cue-field-edge`, its theme's border moved to
|
|
111
|
+
* 3:1 on `surface-1`.
|
|
112
|
+
*
|
|
105
113
|
* **The resting shade** ({@link REST_SHADE_CLASS}, spec §3.10) is the one
|
|
106
114
|
* shadow this recipe draws: the stroke paints a border and a ground and no
|
|
107
115
|
* shadow, so the shade is added as a utility of its own rather than composed
|
|
@@ -110,7 +118,7 @@ import { FormControlSize } from "./_chassis-size.js";
|
|
|
110
118
|
* own `shadow-` utility replaces it through `twMerge`, as it would have
|
|
111
119
|
* replaced nothing before.
|
|
112
120
|
*/
|
|
113
|
-
declare const controlSurface = "w-full rounded-[var(--cue-style-field-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))] border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-sunken),var(--cue-sunken)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[shadow:0_calc(2px*var(--cue-paint-shadow-scale))_calc(4px*var(--cue-paint-shadow-scale))_var(--cue-paint-rest-shade,transparent)] font-[family-name:var(--cue-style-input-face,var(--cue-font-mono))] font-[number:var(--cue-style-body-weight,inherit)] text-fg placeholder:text-fg-subtle outline-none hover:border-[color:var(--cue-style-field-hover-edge,transparent)] focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] focus-visible:outline-solid focus-visible:outline-[length:var(--cue-style-focus-width,var(--cue-focus-ring))] focus-visible:outline-(--cue-focus) focus-visible:outline-offset-[var(--cue-style-field-focus-offset,-1px)] disabled:opacity-[var(--cue-style-disabled-opacity,0.4)] data-[disabled]:opacity-[var(--cue-style-disabled-opacity,0.4)] disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[invalid]:border-danger data-[invalid]:focus-visible:outline-danger";
|
|
121
|
+
declare const controlSurface = "w-full field-ground rounded-[var(--cue-style-field-radius,calc(var(--cue-style-control-radius,var(--cue-radius-control))*var(--cue-radius-scale)))] border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-field-fill,var(--cue-sunken)),var(--cue-style-field-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[shadow:0_calc(2px*var(--cue-paint-shadow-scale))_calc(4px*var(--cue-paint-shadow-scale))_var(--cue-paint-rest-shade,transparent)] font-[family-name:var(--cue-style-input-face,var(--cue-font-mono))] font-[number:var(--cue-style-body-weight,inherit)] text-fg placeholder:text-fg-subtle outline-none hover:border-[color:var(--cue-style-field-hover-edge,transparent)] focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] focus-visible:outline-solid focus-visible:outline-[length:var(--cue-style-focus-width,var(--cue-focus-ring))] focus-visible:outline-(--cue-focus) focus-visible:outline-offset-[var(--cue-style-field-focus-offset,-1px)] disabled:opacity-[var(--cue-style-disabled-opacity,0.4)] data-[disabled]:opacity-[var(--cue-style-disabled-opacity,0.4)] disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[invalid]:border-danger data-[invalid]:focus-visible:outline-danger";
|
|
114
122
|
/**
|
|
115
123
|
* The full recessed-control recipe, one variant per rung of the control ladder.
|
|
116
124
|
*
|
package/dist/forms/_chassis.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { REST_SHADE_CLASS } from "../system/_paint-rest-shade.js";
|
|
2
|
-
import { DATA_DISABLED_OPACITY, DISABLED_OPACITY
|
|
2
|
+
import { DATA_DISABLED_OPACITY, DISABLED_OPACITY } from "../system/_style-disabled.js";
|
|
3
3
|
import { cva } from "../lib/cva.js";
|
|
4
4
|
import { FOCUS_RING_FIELD, FOCUS_RING_GROUP_FIELD } from "../system/_focus-ring-field.js";
|
|
5
5
|
import { CONTROL_STEP_SIZE_BODY, CONTROL_STEP_SIZE_SM } from "../system/_style-control-step.js";
|
|
6
|
-
import { STROKE_SUNKEN } from "../system/_paint-stroke-sunken.js";
|
|
6
|
+
import { FIELD_DISABLED_FILL, STROKE_SUNKEN } from "../system/_paint-stroke-sunken.js";
|
|
7
7
|
import { BODY_WEIGHT } from "../system/_style-body-weight.js";
|
|
8
8
|
import { FIELD_HOVER_EDGE, FIELD_PAD_LG, FIELD_PAD_MD, FIELD_PAD_SM, FIELD_RADIUS, INVALID_EDGE } from "../system/_style-field-chassis.js";
|
|
9
9
|
import { INPUT_FACE } from "../system/_style-input-face.js";
|
|
@@ -109,6 +109,14 @@ import { INPUT_FACE } from "../system/_style-input-face.js";
|
|
|
109
109
|
* set (a hex colour code, a DMX value) restates `font-mono` on top, as
|
|
110
110
|
* `color-picker.tsx`'s hex field does.
|
|
111
111
|
*
|
|
112
|
+
* **`field-ground`** is `styles.css`'s opt-in relative fill (spec
|
|
113
|
+
* 2026-10-08-field-ground R3): inside `[data-field-ground="relative"]` it
|
|
114
|
+
* re-declares this field's own `--cue-sunken` one step off the ground it stands
|
|
115
|
+
* on; anywhere else it compiles to nothing that applies. The same hook carries
|
|
116
|
+
* the contrast edge (cue-ui-60): inside `[data-field-edge="contrast"]` this
|
|
117
|
+
* field's resting stroke reads `--cue-field-edge`, its theme's border moved to
|
|
118
|
+
* 3:1 on `surface-1`.
|
|
119
|
+
*
|
|
112
120
|
* **The resting shade** ({@link REST_SHADE_CLASS}, spec §3.10) is the one
|
|
113
121
|
* shadow this recipe draws: the stroke paints a border and a ground and no
|
|
114
122
|
* shadow, so the shade is added as a utility of its own rather than composed
|
|
@@ -117,7 +125,7 @@ import { INPUT_FACE } from "../system/_style-input-face.js";
|
|
|
117
125
|
* own `shadow-` utility replaces it through `twMerge`, as it would have
|
|
118
126
|
* replaced nothing before.
|
|
119
127
|
*/
|
|
120
|
-
const controlSurface = `w-full ${FIELD_RADIUS} ${STROKE_SUNKEN} ${REST_SHADE_CLASS} ${INPUT_FACE} ${BODY_WEIGHT} text-fg placeholder:text-fg-subtle outline-none ${FIELD_HOVER_EDGE} focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] ${FOCUS_RING_FIELD} ${DISABLED_OPACITY} ${DATA_DISABLED_OPACITY} ${FIELD_DISABLED_FILL} ${INVALID_EDGE} data-[invalid]:focus-visible:outline-danger`;
|
|
128
|
+
const controlSurface = `w-full field-ground ${FIELD_RADIUS} ${STROKE_SUNKEN} ${REST_SHADE_CLASS} ${INPUT_FACE} ${BODY_WEIGHT} text-fg placeholder:text-fg-subtle outline-none ${FIELD_HOVER_EDGE} focus:border-[color:var(--cue-style-focus-edge,var(--cue-border-strong))] ${FOCUS_RING_FIELD} ${DISABLED_OPACITY} ${DATA_DISABLED_OPACITY} ${FIELD_DISABLED_FILL} ${INVALID_EDGE} data-[invalid]:focus-visible:outline-danger`;
|
|
121
129
|
/**
|
|
122
130
|
* The full recessed-control recipe, one variant per rung of the control ladder.
|
|
123
131
|
*
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* @example
|
|
19
19
|
* <Popover.Popup className={cn(overlaySurfaceClasses, className)} />
|
|
20
20
|
*/
|
|
21
|
-
declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
|
|
21
|
+
declare const overlaySurfaceClasses = "rounded-overlay border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))] shadow-[0_0_0_var(--cue-paint-rim-width)_var(--cue-paint-shade-hairline,transparent),0_calc(2px*var(--cue-paint-shadow-scale))_calc(6px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-near,transparent),0_calc(8px*var(--cue-paint-shadow-scale))_calc(24px*var(--cue-paint-shadow-scale))_var(--cue-paint-shadow-spread)_var(--cue-paint-shade-far,rgba(0,0,0,0.4)),inset_0_1px_0_0_var(--cue-paint-sheen-ink,transparent)]";
|
|
22
22
|
/**
|
|
23
23
|
* The full-viewport dim behind a modal surface. `--cue-scrim` is theme-owned —
|
|
24
24
|
* dusk and hivehub tune their own — so the value is never hard-coded here.
|
package/dist/styles.css
CHANGED
|
@@ -133,3 +133,72 @@
|
|
|
133
133
|
clip-path: none;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
/* A container's ground, for the relative field fill (spec 2026-10-08-field-ground R3).
|
|
138
|
+
*
|
|
139
|
+
* Every fill utility a container can paint with says what it painted, as
|
|
140
|
+
* `--cue-ground`, and a control inside `[data-field-ground="relative"]` mixes
|
|
141
|
+
* its fill from the nearest one (`system/_field-ground.ts`). Declaring it is
|
|
142
|
+
* free: nothing outside the opt-in reads it. In `base` so a consumer's own
|
|
143
|
+
* utility or inline `--cue-ground` wins; a container painted any other way — a
|
|
144
|
+
* gradient, an image, a translucent fill — sets `--cue-ground` itself. */
|
|
145
|
+
@layer base {
|
|
146
|
+
.bg-bg {
|
|
147
|
+
--cue-ground: var(--cue-bg);
|
|
148
|
+
}
|
|
149
|
+
.bg-sunken {
|
|
150
|
+
--cue-ground: var(--cue-sunken);
|
|
151
|
+
}
|
|
152
|
+
.bg-surface-1 {
|
|
153
|
+
--cue-ground: var(--cue-surface-1);
|
|
154
|
+
}
|
|
155
|
+
.bg-surface-2 {
|
|
156
|
+
--cue-ground: var(--cue-surface-2);
|
|
157
|
+
}
|
|
158
|
+
.bg-surface-3 {
|
|
159
|
+
--cue-ground: var(--cue-surface-3);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* The relative field fill (spec 2026-10-08-field-ground R3), and opt-in.
|
|
164
|
+
*
|
|
165
|
+
* Nothing happens until an ancestor carries `data-field-ground="relative"`.
|
|
166
|
+
* Inside one, a field that wears `field-ground` — every control the recessed
|
|
167
|
+
* chassis draws does — re-declares its OWN `--cue-sunken` one step off the
|
|
168
|
+
* nearest container's `--cue-ground` (declared above, and by the raised and
|
|
169
|
+
* overlay strokes), toward its text colour: lighter on a dark ground, deeper on
|
|
170
|
+
* a light one, 6% unless `--cue-style-field-step` says otherwise.
|
|
171
|
+
* `--cue-style-field-toward` changes the direction.
|
|
172
|
+
*
|
|
173
|
+
* On the field, never on the ancestor: a custom property resolves its `var()`s
|
|
174
|
+
* where it is declared, so declared on the opt-in element it would freeze at
|
|
175
|
+
* that element's ground and every field below — in a card, in a popover —
|
|
176
|
+
* would fill as if it stood on the page.
|
|
177
|
+
*
|
|
178
|
+
* Re-declaring `sunken` rather than adding a term to the field's fill keeps
|
|
179
|
+
* the chain the stroke already reads, `var(--cue-style-field-fill,
|
|
180
|
+
* var(--cue-sunken))`: a named fill still wins, a disabled field follows, and
|
|
181
|
+
* no field's class grows by more than this utility's name. A consumer's own
|
|
182
|
+
* field can wear `field-ground` too.
|
|
183
|
+
*
|
|
184
|
+
* The contrast edge (cue-ui-60), and opt-in too. A theme's `--cue-border` is a
|
|
185
|
+
* box edge, quiet on purpose, and against `surface-1` it measured 1.07:1 to
|
|
186
|
+
* 2.02:1 in every shipped theme: under WCAG 1.4.11's 3:1 for the line that
|
|
187
|
+
* marks out where to type. `--cue-field-edge` is that border moved toward the
|
|
188
|
+
* ink until it clears 3:1 (each theme writes it per block, as a literal), and
|
|
189
|
+
* inside an ancestor carrying `data-field-edge="contrast"` a field's resting
|
|
190
|
+
* stroke reads it. Declared on the field for the reason the fill is: resolved
|
|
191
|
+
* here, it is the theme the field stands in, island or not. It sets both ends
|
|
192
|
+
* of the stroke, so it wins over a paint pack's gradient edge — the opt-in is
|
|
193
|
+
* a reader's need, and a pack is a look. The hover, focus and invalid edges are
|
|
194
|
+
* their own colours and do not move. Same hook as the relative fill, so no
|
|
195
|
+
* field's class grows for it. */
|
|
196
|
+
@utility field-ground {
|
|
197
|
+
:where([data-field-ground="relative"]) & {
|
|
198
|
+
--cue-sunken: color-mix(in oklab, var(--cue-ground, var(--cue-bg)), var(--cue-style-field-toward, var(--cue-fg)) var(--cue-style-field-step, 6%));
|
|
199
|
+
}
|
|
200
|
+
:where([data-field-edge="contrast"]) & {
|
|
201
|
+
--cue-paint-stroke-from: var(--cue-field-edge, var(--cue-border));
|
|
202
|
+
--cue-paint-stroke-to: var(--cue-field-edge, var(--cue-border));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -12,8 +12,28 @@
|
|
|
12
12
|
* The recipe itself, and why its off state is a flat gradient rather than
|
|
13
13
|
* `none`, is documented once in `./_paint`.
|
|
14
14
|
*
|
|
15
|
+
* **The fill is a name first** (spec 2026-10-08-field-ground R2):
|
|
16
|
+
* `--cue-style-field-fill`, declared on no `:root` for the reason
|
|
17
|
+
* `_style-input-face.ts` gives, so with nothing set this resolves to exactly
|
|
18
|
+
* the `--cue-sunken` it always painted. A style set, or any subtree, names it.
|
|
19
|
+
*
|
|
15
20
|
* @internal Not part of the public API.
|
|
16
21
|
*/
|
|
17
|
-
const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-sunken),var(--cue-sunken)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
|
|
22
|
+
const STROKE_SUNKEN = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-field-fill,var(--cue-sunken)),var(--cue-style-field-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] bg-origin-border [background-clip:padding-box,border-box]";
|
|
23
|
+
/**
|
|
24
|
+
* The same two layers when the field is disabled: the fill read through
|
|
25
|
+
* `style-disabled-fill`, then the field's own fill, then `sunken`, on
|
|
26
|
+
* `:disabled` and on `[data-disabled]`.
|
|
27
|
+
*
|
|
28
|
+
* **Here, beside the resting recipe, and not in `_style-disabled.ts`** with the
|
|
29
|
+
* other disabled recipes (spec 2026-10-08-field-ground R2). Docgen credits every
|
|
30
|
+
* `--cue-*` in a module to each component that imports it, and the button, the
|
|
31
|
+
* checkbox and the switch import that module for their own disabled recipes:
|
|
32
|
+
* left there, all of them would claim a field's fill they never paint. This
|
|
33
|
+
* module is imported by the field chassis alone, so the credit is exact, and
|
|
34
|
+
* the library ships one file per module, so a module of its own would have
|
|
35
|
+
* cost every page that draws a field one more request.
|
|
36
|
+
*/
|
|
37
|
+
const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken))),var(--cue-style-disabled-fill,var(--cue-style-field-fill,var(--cue-sunken)))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
|
|
18
38
|
//#endregion
|
|
19
|
-
export { STROKE_SUNKEN };
|
|
39
|
+
export { FIELD_DISABLED_FILL, STROKE_SUNKEN };
|
|
@@ -12,8 +12,12 @@
|
|
|
12
12
|
* The recipe itself, and why its off state is a flat gradient rather than
|
|
13
13
|
* `none`, is documented once in `./_paint`.
|
|
14
14
|
*
|
|
15
|
+
* It also says what it painted, as `--cue-ground`, for the relative field fill
|
|
16
|
+
* (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
|
|
17
|
+
* takes its fill one step off the nearest container's ground.
|
|
18
|
+
*
|
|
15
19
|
* @internal Not part of the public API.
|
|
16
20
|
*/
|
|
17
|
-
const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box]";
|
|
21
|
+
const STROKE_SURFACE_1 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-surface-1),var(--cue-surface-1)),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-raised-from,var(--cue-paint-stroke-from,var(--cue-border))),var(--cue-paint-stroke-raised-to,var(--cue-paint-stroke-to,var(--cue-border))))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-surface-1)]";
|
|
18
22
|
//#endregion
|
|
19
23
|
export { STROKE_SURFACE_1 };
|
|
@@ -33,8 +33,12 @@
|
|
|
33
33
|
* The recipe itself, and why its off state is a flat gradient rather than
|
|
34
34
|
* `none`, is documented once in `./_paint`.
|
|
35
35
|
*
|
|
36
|
+
* It also says what it painted, as `--cue-ground`, for the relative field fill
|
|
37
|
+
* (spec 2026-10-08-field-ground R3): a field inside `[data-field-ground="relative"]`
|
|
38
|
+
* takes its fill one step off the nearest container's ground.
|
|
39
|
+
*
|
|
36
40
|
* @internal Not part of the public API.
|
|
37
41
|
*/
|
|
38
|
-
const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box]";
|
|
42
|
+
const STROKE_SURFACE_2 = "border-transparent border-(length:--cue-paint-stroke-width) bg-[linear-gradient(var(--cue-style-overlay-ground,var(--cue-surface-2)),var(--cue-style-overlay-ground,var(--cue-surface-2))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-overlay-from,var(--cue-border-overlay)),var(--cue-paint-stroke-overlay-to,var(--cue-border-overlay)))] bg-origin-border [background-clip:padding-box,border-box] [--cue-ground:var(--cue-style-overlay-ground,var(--cue-surface-2))]";
|
|
39
43
|
//#endregion
|
|
40
44
|
export { STROKE_SURFACE_2 };
|
|
@@ -33,12 +33,6 @@ const LABEL_DISABLED_OPACITY = "has-[[data-disabled]]:opacity-[var(--cue-style-d
|
|
|
33
33
|
/** A disabled control label's ink, falling back to the label ink it drew (`LABEL_INK`). */
|
|
34
34
|
const LABEL_DISABLED_INK = "has-[[data-disabled]]:text-[color:var(--cue-style-disabled-ink,var(--cue-style-label-ink,var(--cue-fg-muted)))]";
|
|
35
35
|
/**
|
|
36
|
-
* The field chassis's fill when disabled: its own two layers (`STROKE_SUNKEN`),
|
|
37
|
-
* with the fill layer's `sunken` read through `style-disabled-fill`, on
|
|
38
|
-
* `:disabled` and on `[data-disabled]`.
|
|
39
|
-
*/
|
|
40
|
-
const FIELD_DISABLED_FILL = "disabled:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))] data-[disabled]:bg-[linear-gradient(var(--cue-style-disabled-fill,var(--cue-sunken)),var(--cue-style-disabled-fill,var(--cue-sunken))),linear-gradient(var(--cue-paint-stroke-angle),var(--cue-paint-stroke-from,var(--cue-border)),var(--cue-paint-stroke-to,var(--cue-border)))]";
|
|
41
|
-
/**
|
|
42
36
|
* The button tones' fill and ink when disabled, each falling back to the tone's own resting fill and ink.
|
|
43
37
|
* The two transparent tones keep their ground and take an edge instead (`style-disabled-edge`, falling
|
|
44
38
|
* back to the edge each drew): stripe.com's disabled icon button stays on its ground and draws a 1px rim
|
|
@@ -67,4 +61,4 @@ const CHECK_DISABLED = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,
|
|
|
67
61
|
/** The switch's track when disabled, unchecked and checked, each state keeping its own fallback. */
|
|
68
62
|
const SWITCH_DISABLED_TRACK = "data-[disabled]:bg-[color:var(--cue-style-disabled-fill,var(--cue-surface-3))] data-[disabled]:data-[checked]:bg-[color:var(--cue-style-disabled-fill,var(--cue-accent))]";
|
|
69
63
|
//#endregion
|
|
70
|
-
export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES,
|
|
64
|
+
export { CHECK_DISABLED, DATA_DISABLED_OPACITY, DISABLED_OPACITY, DISABLED_TONES, LABEL_DISABLED_INK, LABEL_DISABLED_OPACITY, SWITCH_DISABLED_TRACK };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, relativeLuminance } from "@cueplusplus/theme-base/contrast";
|
|
2
|
-
export { CONTRAST_REQUIREMENTS, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, relativeLuminance };
|
|
1
|
+
import { CONTRAST_REQUIREMENTS, ContrastCheck, ContrastInput, ContrastReport, ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, edgeToContrast, relativeLuminance } from "@cueplusplus/theme-base/contrast";
|
|
2
|
+
export { CONTRAST_REQUIREMENTS, type ContrastCheck, type ContrastInput, type ContrastReport, type ContrastRequirement, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, edgeToContrast, relativeLuminance };
|
package/dist/theming/contrast.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, relativeLuminance } from "@cueplusplus/theme-base/contrast";
|
|
2
|
-
export { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, relativeLuminance };
|
|
1
|
+
import { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport as contrastReport$1, edgeToContrast, relativeLuminance } from "@cueplusplus/theme-base/contrast";
|
|
2
|
+
export { CONTRAST_REQUIREMENTS, WCAG_AAA_TEXT, WCAG_AA_NON_TEXT, WCAG_AA_TEXT, contrastRatio, contrastReport$1 as contrastReport, edgeToContrast, relativeLuminance };
|