@hraness/design-kit 0.35.0 → 0.35.2
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/AGENT_SETUP.md +4 -0
- package/ARTICLE_COPY.md +19 -7
- package/README.md +8 -2
- package/dist/{chunk-c9mhn3yb.js → chunk-4tgt8egt.js} +18 -13
- package/dist/{chunk-fjf81bww.js → chunk-6ts4955n.js} +8 -34
- package/dist/{chunk-c80g58j1.js → chunk-fmeg5cxf.js} +14 -6
- package/dist/{chunk-8xfssb9c.js → chunk-tzd7177a.js} +138 -81
- package/dist/index.js +1 -1
- package/dist/react/index.js +38 -96
- package/dist/react/platform-install.js +2 -2
- package/dist/react/server.js +2 -2
- package/dist/stylex-manifest.json +1 -1
- package/dist/stylex.css +28 -28
- package/package.json +3 -1
- package/portfolio-inventory.json +1 -1
- package/src/article-html.ts +41 -19
- package/src/react/agent-setup-prompt.stylex.ts +5 -25
- package/src/react/agent-setup-prompt.tsx +9 -18
- package/src/react/article.tsx +32 -14
- package/src/react/design-gallery.tsx +3 -0
- package/src/react/platform-install.stylex.ts +7 -36
- package/src/react/platform-install.tsx +15 -23
- package/src/react/setup-copy-button.stylex.ts +39 -0
- package/src/react/setup-copy-button.tsx +20 -0
package/dist/stylex.css
CHANGED
|
@@ -712,10 +712,6 @@
|
|
|
712
712
|
padding-block: .25rem;
|
|
713
713
|
}
|
|
714
714
|
|
|
715
|
-
.x1267ecw {
|
|
716
|
-
padding-block: .35rem;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
715
|
.x1bilvtl {
|
|
720
716
|
padding-block: .375rem;
|
|
721
717
|
}
|
|
@@ -728,6 +724,10 @@
|
|
|
728
724
|
padding-block: .5rem;
|
|
729
725
|
}
|
|
730
726
|
|
|
727
|
+
.x8fiyw4 {
|
|
728
|
+
padding-block: .65rem;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
731
|
.xo0yzjp {
|
|
732
732
|
padding-block: .75rem;
|
|
733
733
|
}
|
|
@@ -816,10 +816,6 @@
|
|
|
816
816
|
padding-inline: .625rem;
|
|
817
817
|
}
|
|
818
818
|
|
|
819
|
-
.x1f6otoe {
|
|
820
|
-
padding-inline: .65rem;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
819
|
.x1ryrjj2 {
|
|
824
820
|
padding-inline: .75rem;
|
|
825
821
|
}
|
|
@@ -1283,10 +1279,6 @@
|
|
|
1283
1279
|
background-color: color-mix(in srgb,var(--foreground) 8%,transparent);
|
|
1284
1280
|
}
|
|
1285
1281
|
|
|
1286
|
-
.xf7cb96 {
|
|
1287
|
-
background-color: color-mix(in srgb,var(--hraness-marketing-ink, var(--foreground, CanvasText)) 4%,var(--hraness-marketing-surface, var(--surface, Canvas)));
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
1282
|
.xy1scu2 {
|
|
1291
1283
|
background-color: color-mix(in srgb,var(--hraness-marketing-ink, var(--foreground, CanvasText)) 5%,var(--hraness-marketing-background, var(--background, Canvas)));
|
|
1292
1284
|
}
|
|
@@ -2587,6 +2579,10 @@
|
|
|
2587
2579
|
grid-template-columns: repeat(auto-fit,minmax(min(100%,var(--_hraness-marketing-grid-track, 18rem)),1fr));
|
|
2588
2580
|
}
|
|
2589
2581
|
|
|
2582
|
+
.xe92ajh {
|
|
2583
|
+
grid-template-columns: repeat(var(--_hraness-agent-target-wide-columns, var(--_hraness-agent-target-columns, 1)),minmax(0,1fr));
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2590
2586
|
.x3g07o8 {
|
|
2591
2587
|
grid-template-columns: repeat(var(--hraness-marketing-fact-columns, 4),minmax(0,1fr));
|
|
2592
2588
|
}
|
|
@@ -3769,10 +3765,6 @@
|
|
|
3769
3765
|
background-color: color-mix(in srgb,var(--hraness-marketing-ink, var(--foreground, CanvasText)) 6%,transparent);
|
|
3770
3766
|
}
|
|
3771
3767
|
|
|
3772
|
-
.x3oli7v:hover {
|
|
3773
|
-
background-color: color-mix(in srgb,var(--hraness-marketing-ink, var(--foreground, CanvasText)) 9%,var(--hraness-marketing-surface, var(--surface, Canvas)));
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
3768
|
.xjoriyk:hover {
|
|
3777
3769
|
background-color: color-mix(in srgb,var(--hraness-marketing-related-tone, var(--hraness-marketing-ink)) 6%,transparent);
|
|
3778
3770
|
}
|
|
@@ -4376,6 +4368,10 @@
|
|
|
4376
4368
|
border-right-width: 2px;
|
|
4377
4369
|
}
|
|
4378
4370
|
|
|
4371
|
+
.x1h3am7x {
|
|
4372
|
+
border-top-color: color-mix(in srgb,var(--hraness-marketing-ink, var(--foreground, CanvasText)) 12%,transparent);
|
|
4373
|
+
}
|
|
4374
|
+
|
|
4379
4375
|
.x1v8p93f {
|
|
4380
4376
|
border-top-color: #0000;
|
|
4381
4377
|
}
|
|
@@ -4392,6 +4388,10 @@
|
|
|
4392
4388
|
border-top-style: solid;
|
|
4393
4389
|
}
|
|
4394
4390
|
|
|
4391
|
+
.x178xt8z {
|
|
4392
|
+
border-top-width: 1px;
|
|
4393
|
+
}
|
|
4394
|
+
|
|
4395
4395
|
.xamhcws {
|
|
4396
4396
|
border-top-width: 2px;
|
|
4397
4397
|
}
|
|
@@ -4404,10 +4404,6 @@
|
|
|
4404
4404
|
bottom: 0;
|
|
4405
4405
|
}
|
|
4406
4406
|
|
|
4407
|
-
.x1at03k5 {
|
|
4408
|
-
height: .95rem;
|
|
4409
|
-
}
|
|
4410
|
-
|
|
4411
4407
|
.xqtp20y {
|
|
4412
4408
|
height: 0;
|
|
4413
4409
|
}
|
|
@@ -4540,10 +4536,6 @@
|
|
|
4540
4536
|
min-height: 250px;
|
|
4541
4537
|
}
|
|
4542
4538
|
|
|
4543
|
-
.x1in32aq {
|
|
4544
|
-
min-height: 2rem;
|
|
4545
|
-
}
|
|
4546
|
-
|
|
4547
4539
|
.x13gjtz1 {
|
|
4548
4540
|
min-height: 3.5rem;
|
|
4549
4541
|
}
|
|
@@ -4628,10 +4620,6 @@
|
|
|
4628
4620
|
top: var(--hraness-design-procedural-ripple-y);
|
|
4629
4621
|
}
|
|
4630
4622
|
|
|
4631
|
-
.x2tfi8i {
|
|
4632
|
-
width: .95rem;
|
|
4633
|
-
}
|
|
4634
|
-
|
|
4635
4623
|
.xnalus7 {
|
|
4636
4624
|
width: 0;
|
|
4637
4625
|
}
|
|
@@ -5863,6 +5851,18 @@
|
|
|
5863
5851
|
}
|
|
5864
5852
|
}
|
|
5865
5853
|
|
|
5854
|
+
@container hraness-agent-setup (width >= 20rem) {
|
|
5855
|
+
.x6fuqh4.x6fuqh4 {
|
|
5856
|
+
--_hraness-agent-target-columns: 2;
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
|
|
5860
|
+
@container hraness-agent-setup (width >= 34rem) {
|
|
5861
|
+
.x1ii36b5.x1ii36b5 {
|
|
5862
|
+
--_hraness-agent-target-wide-columns: 3;
|
|
5863
|
+
}
|
|
5864
|
+
}
|
|
5865
|
+
|
|
5866
5866
|
@container hraness-platform-install (width <= 30rem) {
|
|
5867
5867
|
.x1rp0mr4.x1rp0mr4 {
|
|
5868
5868
|
--_hraness-platform-install-narrow-tab-font-size: .8125rem;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hraness/design-kit",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.2",
|
|
4
4
|
"description": "React components and CSS for Hraness apps and product sites: application shells, marketing sections, charts, themes, effects, and syntax highlighting. Built on @hraness/ui.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -300,6 +300,8 @@
|
|
|
300
300
|
"src/agent-setup.ts",
|
|
301
301
|
"src/react/agent-setup-prompt.tsx",
|
|
302
302
|
"src/react/agent-setup-prompt.stylex.ts",
|
|
303
|
+
"src/react/setup-copy-button.tsx",
|
|
304
|
+
"src/react/setup-copy-button.stylex.ts",
|
|
303
305
|
"AGENT_SETUP.md",
|
|
304
306
|
"src/react/marketing-comparison.tsx",
|
|
305
307
|
"src/react/marketing-comparison.stylex.ts",
|
package/portfolio-inventory.json
CHANGED
package/src/article-html.ts
CHANGED
|
@@ -83,6 +83,8 @@ export interface ArticleHtmlInput {
|
|
|
83
83
|
readonly id?: string;
|
|
84
84
|
readonly provenance: ArticleProvenanceRecord | null;
|
|
85
85
|
readonly published: ArticleIsoDate;
|
|
86
|
+
/** Show publication and update dates. Defaults to true; dates remain required and validated when hidden. */
|
|
87
|
+
readonly showDates?: boolean;
|
|
86
88
|
readonly toc?: readonly ArticleTocItem[];
|
|
87
89
|
readonly tocLabel?: string;
|
|
88
90
|
readonly updated?: ArticleIsoDate;
|
|
@@ -91,7 +93,7 @@ export interface ArticleHtmlInput {
|
|
|
91
93
|
/** The static equivalent of `MarketingArticle`. */
|
|
92
94
|
export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
93
95
|
const headingId = input.headingId ?? "article-title";
|
|
94
|
-
const { published, updated } = input;
|
|
96
|
+
const { published, updated, showDates = true } = input;
|
|
95
97
|
assertArticleDates(updated === undefined ? { published } : { published, updated });
|
|
96
98
|
const toc = input.toc ?? [];
|
|
97
99
|
for (const item of toc) {
|
|
@@ -99,16 +101,16 @@ export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
|
99
101
|
}
|
|
100
102
|
const tocId = `${headingId}-contents`;
|
|
101
103
|
const meta = [
|
|
102
|
-
input.author === undefined ? "" : renderArticleBylineHtml(input.author)
|
|
103
|
-
dateHtml("Published", published),
|
|
104
|
-
updated
|
|
105
|
-
].join(
|
|
104
|
+
input.author === undefined ? "" : renderArticleBylineHtml(input.author),
|
|
105
|
+
showDates ? dateHtml("Published", published) : "",
|
|
106
|
+
showDates && updated !== undefined ? dateHtml("Updated", updated) : "",
|
|
107
|
+
].filter(Boolean).join(SEPARATOR);
|
|
106
108
|
const header = [
|
|
107
109
|
'<header class="plain-publication__article-header">',
|
|
108
110
|
present(input.eyebrow) ? `<p class="plain-publication__eyebrow">${escapeArticleHtml(input.eyebrow)}</p>` : "",
|
|
109
111
|
`<h1 id="${escapeArticleHtml(headingId)}">${escapeArticleHtml(input.heading)}</h1>`,
|
|
110
112
|
present(input.dek) ? `<p class="plain-publication__article-dek">${escapeArticleHtml(input.dek)}</p>` : "",
|
|
111
|
-
`<p class="plain-publication__article-meta">${meta}</p>`,
|
|
113
|
+
meta === "" ? "" : `<p class="plain-publication__article-meta">${meta}</p>`,
|
|
112
114
|
input.provenance === null ? "" : renderArticleProvenanceHtml(input.provenance),
|
|
113
115
|
"</header>",
|
|
114
116
|
].join("");
|
|
@@ -135,21 +137,35 @@ export function renderArticleHtml(input: ArticleHtmlInput): string {
|
|
|
135
137
|
export function renderArticleSourcesHtml({
|
|
136
138
|
heading = ARTICLE_SOURCES_HEADING,
|
|
137
139
|
headingId = "article-sources",
|
|
140
|
+
showDates = true,
|
|
138
141
|
sources,
|
|
139
|
-
}: Readonly<{
|
|
142
|
+
}: Readonly<{
|
|
143
|
+
heading?: string;
|
|
144
|
+
headingId?: string;
|
|
145
|
+
/** Show check dates. Defaults to true; checkedOn remains required and validated when hidden. */
|
|
146
|
+
showDates?: boolean;
|
|
147
|
+
sources: readonly ArticleSourceItem[];
|
|
148
|
+
}>): string {
|
|
140
149
|
if (sources.length === 0) return "";
|
|
141
|
-
for (const source of sources)
|
|
150
|
+
for (const source of sources) {
|
|
151
|
+
assertArticleHref(source.href);
|
|
152
|
+
formatArticleDate(source.checkedOn);
|
|
153
|
+
}
|
|
142
154
|
return [
|
|
143
155
|
`<section aria-labelledby="${escapeArticleHtml(headingId)}" class="plain-publication__sources">`,
|
|
144
156
|
`<h2 id="${escapeArticleHtml(headingId)}">${escapeArticleHtml(heading)}</h2>`,
|
|
145
157
|
"<ol>",
|
|
146
|
-
...sources.map((source) =>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
...sources.map((source) => {
|
|
159
|
+
const meta = [
|
|
160
|
+
present(source.publisher) ? escapeArticleHtml(source.publisher) : "",
|
|
161
|
+
showDates ? dateHtml("Checked", source.checkedOn) : "",
|
|
162
|
+
].filter(Boolean).join(SEPARATOR);
|
|
163
|
+
return [
|
|
164
|
+
`<li><a href="${escapeArticleHtml(source.href)}">${escapeArticleHtml(source.title)}</a>`,
|
|
165
|
+
meta === "" ? "" : `<span>${meta}</span>`,
|
|
166
|
+
"</li>",
|
|
167
|
+
].join("");
|
|
168
|
+
}),
|
|
153
169
|
"</ol></section>",
|
|
154
170
|
].join("");
|
|
155
171
|
}
|
|
@@ -228,6 +244,7 @@ export function renderArticleIndexHtml({
|
|
|
228
244
|
headingLevel = 2,
|
|
229
245
|
id,
|
|
230
246
|
items,
|
|
247
|
+
showDates = true,
|
|
231
248
|
summary,
|
|
232
249
|
}: Readonly<{
|
|
233
250
|
className?: string;
|
|
@@ -236,6 +253,8 @@ export function renderArticleIndexHtml({
|
|
|
236
253
|
headingLevel?: 1 | 2 | 3 | 4 | 5;
|
|
237
254
|
id?: string;
|
|
238
255
|
items: readonly ArticleIndexItem[];
|
|
256
|
+
/** Show publication and update dates. Defaults to true; item dates remain required and validated when hidden. */
|
|
257
|
+
showDates?: boolean;
|
|
239
258
|
summary?: string;
|
|
240
259
|
}>): string {
|
|
241
260
|
if (![1, 2, 3, 4, 5].includes(headingLevel)) throw new RangeError("Article index heading level must be 1 to 5.");
|
|
@@ -259,10 +278,13 @@ export function renderArticleIndexHtml({
|
|
|
259
278
|
present(item.eyebrow) ? `<p class="plain-publication__entry-label">${escapeArticleHtml(item.eyebrow)}</p>` : "",
|
|
260
279
|
`<${entry} class="plain-publication__entry-title"><a href="${escapeArticleHtml(item.href)}">${escapeArticleHtml(item.title)}</a></${entry}>`,
|
|
261
280
|
`<p class="plain-publication__entry-dek">${escapeArticleHtml(item.dek)}</p>`,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
281
|
+
showDates ? [
|
|
282
|
+
'<p class="plain-publication__entry-meta">',
|
|
283
|
+
dateHtml("Published", item.published),
|
|
284
|
+
item.updated === undefined ? "" : SEPARATOR + dateHtml("Updated", item.updated),
|
|
285
|
+
"</p>",
|
|
286
|
+
].join("") : "",
|
|
287
|
+
"</article>",
|
|
266
288
|
].join("")),
|
|
267
289
|
"</div></section>",
|
|
268
290
|
].join("");
|
|
@@ -89,39 +89,19 @@ export const agentSetupStyles = stylex.create({
|
|
|
89
89
|
":hover": { color: ink },
|
|
90
90
|
"::marker": { content: '""' },
|
|
91
91
|
},
|
|
92
|
-
copy: {
|
|
93
|
-
alignItems: "center",
|
|
94
|
-
appearance: "none",
|
|
95
|
-
backgroundColor: { default: background, ":hover": surface, [forced]: "ButtonFace" },
|
|
96
|
-
borderColor: { default: line, [forced]: "ButtonText" },
|
|
97
|
-
borderRadius: "0.5rem",
|
|
98
|
-
borderStyle: "solid",
|
|
99
|
-
borderWidth: "1px",
|
|
100
|
-
boxSizing: "border-box",
|
|
101
|
-
color: { default: ink, [forced]: "ButtonText" },
|
|
102
|
-
cursor: "pointer",
|
|
103
|
-
display: { default: "inline-flex", [noScript]: "none" },
|
|
104
|
-
flexShrink: 0,
|
|
105
|
-
fontFamily: "inherit",
|
|
106
|
-
fontSize: "0.8125rem",
|
|
107
|
-
gap: "0.4rem",
|
|
108
|
-
justifyContent: "center",
|
|
109
|
-
lineHeight: 1,
|
|
110
|
-
margin: 0,
|
|
111
|
-
minBlockSize: "2.75rem",
|
|
112
|
-
outline: { default: "none", ":focus-visible": `2px solid ${focus}` },
|
|
113
|
-
outlineOffset: "2px",
|
|
114
|
-
padding: "0.65rem 0.8rem",
|
|
115
|
-
},
|
|
116
92
|
copyOverlay: {
|
|
117
93
|
insetBlockEnd: "0.5rem",
|
|
118
94
|
insetInlineEnd: "0.5rem",
|
|
119
95
|
position: "absolute",
|
|
120
96
|
},
|
|
121
|
-
copyIcon: { blockSize: "1rem", flexShrink: 0, inlineSize: "1rem" },
|
|
122
97
|
targets: { display: "grid", gap: "0.6rem", minInlineSize: 0 },
|
|
123
98
|
targetsLabel: { color: { default: muted, [forced]: "CanvasText" }, fontSize: "0.8125rem", fontWeight: 500, margin: 0 },
|
|
124
99
|
targetList: { display: "grid", gap: "0.45rem", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 10rem), 1fr))", listStyle: "none", margin: 0, padding: 0 },
|
|
100
|
+
targetGrid: {
|
|
101
|
+
gridTemplateColumns: "repeat(var(--_hraness-agent-target-wide-columns, var(--_hraness-agent-target-columns, 1)), minmax(0, 1fr))",
|
|
102
|
+
"--_hraness-agent-target-columns": { default: null, "@container hraness-agent-setup (min-width: 20rem)": "2" },
|
|
103
|
+
"--_hraness-agent-target-wide-columns": { default: null, "@container hraness-agent-setup (min-width: 34rem)": "3" },
|
|
104
|
+
},
|
|
125
105
|
target: {
|
|
126
106
|
alignItems: "center",
|
|
127
107
|
backgroundColor: { default: surface, ":hover": background, [forced]: "ButtonFace" },
|
|
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useId, useRef, useState, type KeyboardEvent, ty
|
|
|
5
5
|
import type { ProviderMarkDescriptor } from "../provider-marks.js";
|
|
6
6
|
import { agentSetupClassName as cx } from "./agent-setup-prompt.stylex.js";
|
|
7
7
|
import { ProviderMark } from "./provider-mark.js";
|
|
8
|
+
import { SetupCopyButton } from "./setup-copy-button.js";
|
|
8
9
|
import { SyntaxCode } from "./syntax-code.js";
|
|
9
10
|
|
|
10
11
|
export interface AgentSetupTarget {
|
|
@@ -20,6 +21,8 @@ export interface AgentSetupPromptProps {
|
|
|
20
21
|
readonly prompt: string;
|
|
21
22
|
readonly label?: string;
|
|
22
23
|
readonly targets?: readonly AgentSetupTarget[];
|
|
24
|
+
/** Keep provider actions below the prompt, or place them beside it in wide containers. */
|
|
25
|
+
readonly targetsPlacement?: "responsive" | "below";
|
|
23
26
|
readonly className?: string;
|
|
24
27
|
/** Runs once after a successful copy; callback failures do not change copy feedback. */
|
|
25
28
|
readonly onCopied?: () => void;
|
|
@@ -186,21 +189,8 @@ function useCopy(text: string, subject: string, fallback: () => HTMLElement | nu
|
|
|
186
189
|
return { state, message, copy };
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
function CopyGlyph({ copied }: Readonly<{ copied: boolean }>) {
|
|
190
|
-
return (
|
|
191
|
-
<svg aria-hidden="true" className={cx(["copyIcon"])} fill="none" focusable="false" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} viewBox="0 0 24 24">
|
|
192
|
-
{copied ? <path d="M5 12.5l4.5 4.5L19 7.5" /> : <><rect height="12" rx="2" width="12" x="8" y="8" /><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" /></>}
|
|
193
|
-
</svg>
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
192
|
function CopyButton({ copy, overlay = false, state, subject }: Readonly<{ copy: () => Promise<boolean>; overlay?: boolean; state: CopyState; subject: string }>) {
|
|
198
|
-
return (
|
|
199
|
-
<button aria-busy={state === "copying" || undefined} aria-label={`${state === "copied" ? "Copied" : "Copy"} ${subject}`} className={cx(["copy", overlay && "copyOverlay"])} data-copy-state={state} disabled={state === "copying"} onClick={() => { void copy(); }} type="button">
|
|
200
|
-
<CopyGlyph copied={state === "copied"} />
|
|
201
|
-
<span>{state === "copied" ? "Copied" : state === "copying" ? "Copying" : state === "failed" ? "Copy failed" : "Copy"}</span>
|
|
202
|
-
</button>
|
|
203
|
-
);
|
|
193
|
+
return <SetupCopyButton className={cx([overlay && "copyOverlay"], "hraness-agent-setup__copy")} iconClassName="hraness-agent-setup__copyIcon" onCopy={() => { void copy(); }} state={state} subject={subject} />;
|
|
204
194
|
}
|
|
205
195
|
|
|
206
196
|
function CopyStatus({ children }: Readonly<{ children: ReactNode }>) {
|
|
@@ -231,10 +221,11 @@ function reserveTargetTab(owner: Window | null): Window | null {
|
|
|
231
221
|
}
|
|
232
222
|
|
|
233
223
|
/** A quiet prompt preview with the complete source behind a native disclosure. */
|
|
234
|
-
export function AgentSetupPrompt({ prompt, label = "Agent setup", targets = [], className, onCopied }: AgentSetupPromptProps) {
|
|
224
|
+
export function AgentSetupPrompt({ prompt, label = "Agent setup", targets = [], targetsPlacement = "responsive", className, onCopied }: AgentSetupPromptProps) {
|
|
235
225
|
assertText(prompt, "Agent setup prompt");
|
|
236
226
|
assertText(label, "Agent setup label");
|
|
237
227
|
assertEntries(targets, "Agent setup target");
|
|
228
|
+
if (targetsPlacement !== "responsive" && targetsPlacement !== "below") throw new RangeError("Unsupported agent setup targets placement.");
|
|
238
229
|
for (const target of targets) {
|
|
239
230
|
assertHref(target.href);
|
|
240
231
|
if (target.mode !== undefined && target.mode !== "prefill" && target.mode !== "copy-and-open") throw new RangeError("Unsupported agent setup target mode.");
|
|
@@ -313,8 +304,8 @@ export function AgentSetupPrompt({ prompt, label = "Agent setup", targets = [],
|
|
|
313
304
|
}, []);
|
|
314
305
|
|
|
315
306
|
return (
|
|
316
|
-
<section aria-label={label} className={cx(["root"], className)} data-hraness-agent-setup-prompt="">
|
|
317
|
-
<div className={cx(["layout", targets.length > 0 && "withTargets"])}>
|
|
307
|
+
<section aria-label={label} className={cx(["root"], className)} data-hraness-agent-setup-prompt="" data-targets-placement={targetsPlacement}>
|
|
308
|
+
<div className={cx(["layout", targets.length > 0 && targetsPlacement === "responsive" && "withTargets"])}>
|
|
318
309
|
<div className={cx(["frame"])} data-copy-state={state}>
|
|
319
310
|
<div className={cx(["preview"])} hidden={expanded}>
|
|
320
311
|
<pre aria-label={`${label} preview`} className={cx(["pre", "previewText"])}>{prompt}</pre>
|
|
@@ -330,7 +321,7 @@ export function AgentSetupPrompt({ prompt, label = "Agent setup", targets = [],
|
|
|
330
321
|
{targets.length === 0 ? null : (
|
|
331
322
|
<aside aria-labelledby={`${id}-targets`} className={cx(["targets"])}>
|
|
332
323
|
<p className={cx(["targetsLabel"])} id={`${id}-targets`}>Open in</p>
|
|
333
|
-
<ul className={cx(["targetList"])}>
|
|
324
|
+
<ul className={cx(["targetList", targetsPlacement === "below" && "targetGrid"])}>
|
|
334
325
|
{targets.map((target) => (
|
|
335
326
|
<li key={target.id}>
|
|
336
327
|
<a aria-busy={target.mode === "copy-and-open" && state === "copying" || undefined} aria-disabled={target.mode === "copy-and-open" && state === "copying" || undefined} aria-label={target.mode === "copy-and-open" ? `Copy prompt and open ${target.label}` : undefined} className={cx(["target"])} data-agent-target={target.id} data-agent-target-mode={target.mode ?? "prefill"} href={target.href} onAuxClick={target.mode === "copy-and-open" ? (event) => { copyAndOpen(event, target); } : undefined} onClick={target.mode === "copy-and-open" ? (event) => { copyAndOpen(event, target); } : undefined} rel="noopener noreferrer" target="_blank">
|
package/src/react/article.tsx
CHANGED
|
@@ -93,6 +93,8 @@ export interface MarketingArticleProps {
|
|
|
93
93
|
/** Required so every article decides its drafting note. Pass null only for writing with no AI involvement and no review record. */
|
|
94
94
|
readonly provenance: ArticleProvenanceRecord | null;
|
|
95
95
|
readonly published: ArticleIsoDate;
|
|
96
|
+
/** Show publication and update dates. Defaults to true; dates remain required and validated when hidden. */
|
|
97
|
+
readonly showDates?: boolean;
|
|
96
98
|
readonly toc?: readonly ArticleTocItem[];
|
|
97
99
|
readonly tocLabel?: string;
|
|
98
100
|
readonly updated?: ArticleIsoDate;
|
|
@@ -116,6 +118,7 @@ export function MarketingArticle({
|
|
|
116
118
|
id,
|
|
117
119
|
provenance,
|
|
118
120
|
published,
|
|
121
|
+
showDates = true,
|
|
119
122
|
toc,
|
|
120
123
|
tocLabel = ARTICLE_TOC_LABEL,
|
|
121
124
|
updated,
|
|
@@ -138,11 +141,13 @@ export function MarketingArticle({
|
|
|
138
141
|
{eyebrow === undefined || eyebrow === "" ? null : <p className="plain-publication__eyebrow">{eyebrow}</p>}
|
|
139
142
|
<h1 id={headingId}>{heading}</h1>
|
|
140
143
|
{dek === undefined || dek === "" ? null : <p className="plain-publication__article-dek">{dek}</p>}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
{author === undefined && !showDates ? null : (
|
|
145
|
+
<p className="plain-publication__article-meta">
|
|
146
|
+
{author === undefined ? null : <><ArticleByline author={author} />{showDates ? <Separator /> : null}</>}
|
|
147
|
+
{showDates ? <ArticleDate label="Published" value={published} /> : null}
|
|
148
|
+
{!showDates || updated === undefined ? null : <><Separator /><ArticleDate label="Updated" value={updated} /></>}
|
|
149
|
+
</p>
|
|
150
|
+
)}
|
|
146
151
|
{provenance === null ? null : <ArticleProvenance provenance={provenance} />}
|
|
147
152
|
</header>
|
|
148
153
|
<div className="plain-publication__article-layout">
|
|
@@ -165,14 +170,20 @@ export function MarketingArticle({
|
|
|
165
170
|
export function ArticleSources({
|
|
166
171
|
heading = ARTICLE_SOURCES_HEADING,
|
|
167
172
|
headingId = "article-sources",
|
|
173
|
+
showDates = true,
|
|
168
174
|
sources,
|
|
169
175
|
}: Readonly<{
|
|
170
176
|
heading?: string;
|
|
171
177
|
headingId?: string;
|
|
178
|
+
/** Show check dates. Defaults to true; checkedOn remains required and validated when hidden. */
|
|
179
|
+
showDates?: boolean;
|
|
172
180
|
sources: readonly ArticleSourceItem[];
|
|
173
181
|
}>) {
|
|
174
182
|
if (sources.length === 0) return null;
|
|
175
|
-
for (const source of sources)
|
|
183
|
+
for (const source of sources) {
|
|
184
|
+
assertArticleHref(source.href);
|
|
185
|
+
formatArticleDate(source.checkedOn);
|
|
186
|
+
}
|
|
176
187
|
return (
|
|
177
188
|
<section aria-labelledby={headingId} className="plain-publication__sources">
|
|
178
189
|
<h2 id={headingId}>{heading}</h2>
|
|
@@ -180,10 +191,12 @@ export function ArticleSources({
|
|
|
180
191
|
{sources.map((source) => (
|
|
181
192
|
<li key={`${source.href}-${source.title}`}>
|
|
182
193
|
<a href={source.href}>{source.title}</a>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
{!showDates && (source.publisher === undefined || source.publisher === "") ? null : (
|
|
195
|
+
<span>
|
|
196
|
+
{source.publisher === undefined || source.publisher === "" ? null : <>{source.publisher}{showDates ? <Separator /> : null}</>}
|
|
197
|
+
{showDates ? <ArticleDate label="Checked" value={source.checkedOn} /> : null}
|
|
198
|
+
</span>
|
|
199
|
+
)}
|
|
187
200
|
</li>
|
|
188
201
|
))}
|
|
189
202
|
</ol>
|
|
@@ -253,6 +266,7 @@ export function ArticleIndex({
|
|
|
253
266
|
headingLevel = 2,
|
|
254
267
|
id,
|
|
255
268
|
items,
|
|
269
|
+
showDates = true,
|
|
256
270
|
summary,
|
|
257
271
|
}: Readonly<{
|
|
258
272
|
className?: string;
|
|
@@ -261,6 +275,8 @@ export function ArticleIndex({
|
|
|
261
275
|
headingLevel?: 1 | 2 | 3 | 4 | 5;
|
|
262
276
|
id?: string;
|
|
263
277
|
items: readonly ArticleIndexItem[];
|
|
278
|
+
/** Show publication and update dates. Defaults to true; item dates remain required and validated when hidden. */
|
|
279
|
+
showDates?: boolean;
|
|
264
280
|
summary?: string;
|
|
265
281
|
}>) {
|
|
266
282
|
if (![1, 2, 3, 4, 5].includes(headingLevel)) throw new RangeError("Article index heading level must be 1 to 5.");
|
|
@@ -290,10 +306,12 @@ export function ArticleIndex({
|
|
|
290
306
|
{item.eyebrow === undefined || item.eyebrow === "" ? null : <p className="plain-publication__entry-label">{item.eyebrow}</p>}
|
|
291
307
|
<EntryTag className="plain-publication__entry-title"><a href={item.href}>{item.title}</a></EntryTag>
|
|
292
308
|
<p className="plain-publication__entry-dek">{item.dek}</p>
|
|
293
|
-
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
309
|
+
{showDates ? (
|
|
310
|
+
<p className="plain-publication__entry-meta">
|
|
311
|
+
<ArticleDate label="Published" value={item.published} />
|
|
312
|
+
{item.updated === undefined ? null : <><Separator /><ArticleDate label="Updated" value={item.updated} /></>}
|
|
313
|
+
</p>
|
|
314
|
+
) : null}
|
|
297
315
|
</article>
|
|
298
316
|
))}
|
|
299
317
|
</div>
|
|
@@ -401,6 +401,7 @@ export function DesignSystemGallery({
|
|
|
401
401
|
<MarketingSection heading="Ask your agent to set it up." headingId="gallery-agent-setup" headingLevel={3} label="Agent setup">
|
|
402
402
|
<AgentSetupPrompt
|
|
403
403
|
label="Relay agent setup"
|
|
404
|
+
targetsPlacement="below"
|
|
404
405
|
prompt={"Install Relay from https://relay.example/install.sh.\nRead the project instructions before making changes.\nRun relay init in this workspace and create a job named sample.\nRun the job, then show me its log."}
|
|
405
406
|
targets={[
|
|
406
407
|
{ href: "https://claude.ai/new", id: "claude", label: "Claude", mark: "claudecode", mode: "copy-and-open" },
|
|
@@ -677,6 +678,7 @@ export function DesignSystemGallery({
|
|
|
677
678
|
after={(
|
|
678
679
|
<ArticleSources
|
|
679
680
|
headingId="gallery-article-sources"
|
|
681
|
+
showDates={false}
|
|
680
682
|
sources={[
|
|
681
683
|
{ checkedOn: "2026-09-20", href: "#articles", publisher: "Relay", title: "Relay 2.4 release notes" },
|
|
682
684
|
]}
|
|
@@ -692,6 +694,7 @@ export function DesignSystemGallery({
|
|
|
692
694
|
review: { reviewer: "an independent AI editorial review", reviewerType: "ai" },
|
|
693
695
|
}}
|
|
694
696
|
published="2026-09-10"
|
|
697
|
+
showDates={false}
|
|
695
698
|
toc={[
|
|
696
699
|
{ href: "#gallery-article-problem", label: "The problem" },
|
|
697
700
|
{ href: "#gallery-article-approach", label: "The approach" },
|
|
@@ -221,40 +221,6 @@ export const platformInstallStyles = stylex.create({
|
|
|
221
221
|
fontSize: "inherit",
|
|
222
222
|
padding: 0,
|
|
223
223
|
},
|
|
224
|
-
copy: {
|
|
225
|
-
alignItems: "center",
|
|
226
|
-
appearance: "none",
|
|
227
|
-
backgroundColor: {
|
|
228
|
-
default: `color-mix(in srgb, ${ink} 4%, ${surface})`,
|
|
229
|
-
":hover": `color-mix(in srgb, ${ink} 9%, ${surface})`,
|
|
230
|
-
[forced]: "ButtonFace",
|
|
231
|
-
},
|
|
232
|
-
borderColor: { default: line, [forced]: "ButtonText" },
|
|
233
|
-
borderRadius: "0.5rem",
|
|
234
|
-
borderStyle: "solid",
|
|
235
|
-
borderWidth: "1px",
|
|
236
|
-
boxSizing: "border-box",
|
|
237
|
-
color: { default: ink, [forced]: "ButtonText" },
|
|
238
|
-
cursor: "pointer",
|
|
239
|
-
display: { default: "inline-flex", [noScript]: "none" },
|
|
240
|
-
flexShrink: 0,
|
|
241
|
-
fontFamily: textFont,
|
|
242
|
-
fontSize: "0.8125rem",
|
|
243
|
-
fontWeight: 500,
|
|
244
|
-
gap: "0.35rem",
|
|
245
|
-
lineHeight: 1,
|
|
246
|
-
margin: 0,
|
|
247
|
-
minBlockSize: { default: "2rem", "@media (pointer: coarse)": "2.75rem" },
|
|
248
|
-
outline: { default: "none", ":focus-visible": `2px solid ${focusRing}` },
|
|
249
|
-
outlineOffset: "1px",
|
|
250
|
-
paddingBlock: "0.35rem",
|
|
251
|
-
paddingInline: "0.65rem",
|
|
252
|
-
},
|
|
253
|
-
copyIcon: {
|
|
254
|
-
blockSize: "0.95rem",
|
|
255
|
-
flexShrink: 0,
|
|
256
|
-
inlineSize: "0.95rem",
|
|
257
|
-
},
|
|
258
224
|
alternatives: {
|
|
259
225
|
display: "grid",
|
|
260
226
|
gap: "0.6rem",
|
|
@@ -269,6 +235,13 @@ export const platformInstallStyles = stylex.create({
|
|
|
269
235
|
lineHeight: 1.5,
|
|
270
236
|
margin: 0,
|
|
271
237
|
},
|
|
238
|
+
commandNote: {
|
|
239
|
+
borderBlockStartColor: { default: line, [forced]: "CanvasText" },
|
|
240
|
+
borderBlockStartStyle: "solid",
|
|
241
|
+
borderBlockStartWidth: "1px",
|
|
242
|
+
paddingBlock: "0.65rem",
|
|
243
|
+
paddingInline: "0.95rem",
|
|
244
|
+
},
|
|
272
245
|
unavailable: {
|
|
273
246
|
borderColor: { default: line, [forced]: "CanvasText" },
|
|
274
247
|
borderRadius: "0.75rem",
|
|
@@ -359,8 +332,6 @@ const hooks: Partial<Record<Part, string>> = {
|
|
|
359
332
|
code: "hraness-platform-install__code",
|
|
360
333
|
command: "hraness-platform-install__command",
|
|
361
334
|
commandBar: "hraness-platform-install__command-bar",
|
|
362
|
-
copy: "hraness-platform-install__copy",
|
|
363
|
-
copyIcon: "hraness-platform-install__copy-icon",
|
|
364
335
|
icon: "hraness-platform-icon",
|
|
365
336
|
markSymbols: "hraness-platform-install__marks",
|
|
366
337
|
note: "hraness-platform-install__note",
|