@salesforcedevs/docs-components 1.33.2-alpha-iphone-scroll-7 → 1.33.2-llms-txt-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/package.json +1 -1
- package/src/modules/doc/contentActionToolbar/contentActionToolbar.css +17 -24
- package/src/modules/doc/contentActionToolbar/contentActionToolbar.html +13 -51
- package/src/modules/doc/contentActionToolbar/contentActionToolbar.ts +153 -24
- package/src/modules/doc/contentActionToolbar/contentActionToolbarMocks.ts +20 -5
- package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +0 -18
package/package.json
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
@import "dxHelpers/reset";
|
|
2
2
|
|
|
3
3
|
:host {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/* Match the Figma menu rows: Body 2 Regular, gray-10 (the dropdown option
|
|
5
|
+
default is bold + blue), with 24px (spacing-lg) horizontal padding on
|
|
6
|
+
both option rows and subheaders. These custom properties inherit through
|
|
7
|
+
the composed tree down to each dx-dropdown-option. */
|
|
8
|
+
--dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
|
|
9
|
+
--dx-c-dropdown-option-font-weight: var(--dx-g-font-normal);
|
|
10
|
+
--dx-c-dropdown-option-padding-horizontal: var(--dx-g-spacing-lg);
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
margin-bottom: var(--dx-g-spacing-lg);
|
|
12
|
-
}
|
|
12
|
+
/* Popover only needs 8px vertical padding; horizontal is 0 since the
|
|
13
|
+
options already carry 24px each side. Inherits down to the dropdown's
|
|
14
|
+
dx-popover. */
|
|
15
|
+
--dx-c-popover-padding: var(--dx-g-spacing-sm) 0;
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
--dx-c-
|
|
18
|
-
}
|
|
17
|
+
/* Size the menu to its widest option instead of the default fixed 168px,
|
|
18
|
+
so labels like "Copy as Markdown" aren't clipped or awkwardly narrow.
|
|
19
|
+
Inherits down to the dropdown's dx-popover. */
|
|
20
|
+
--dx-c-popover-width: max-content;
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
height: var(--dx-g-spacing-md);
|
|
23
|
-
background-color: var(--dx-g-gray-70);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@media screen and (max-width: 480px) {
|
|
27
|
-
.toolbar-button_copy-url,
|
|
28
|
-
.divider_copy-url {
|
|
29
|
-
display: none;
|
|
30
|
-
}
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
margin-bottom: var(--dx-g-spacing-lg);
|
|
31
24
|
}
|
|
@@ -1,53 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{copyMarkdownButtonText}
|
|
16
|
-
</dx-button>
|
|
17
|
-
</dx-tooltip>
|
|
18
|
-
|
|
19
|
-
<div class="divider"></div>
|
|
20
|
-
|
|
21
|
-
<dx-button
|
|
22
|
-
class="toolbar-button"
|
|
23
|
-
variant="inline"
|
|
24
|
-
size="small"
|
|
25
|
-
icon-sprite="utility"
|
|
26
|
-
icon-symbol="new_window"
|
|
27
|
-
icon-size="medium"
|
|
28
|
-
icon-position="right"
|
|
29
|
-
aria-label={viewMarkdownButtonText}
|
|
30
|
-
onclick={handleViewMarkdown}
|
|
31
|
-
>
|
|
32
|
-
{viewMarkdownButtonText}
|
|
33
|
-
</dx-button>
|
|
34
|
-
|
|
35
|
-
<div class="divider divider_copy-url"></div>
|
|
36
|
-
|
|
37
|
-
<dx-tooltip placement="top-right" label={copyUrlLabel}>
|
|
38
|
-
<dx-button
|
|
39
|
-
class="toolbar-button toolbar-button_copy-url"
|
|
40
|
-
variant="inline"
|
|
41
|
-
size="small"
|
|
42
|
-
icon-sprite="utility"
|
|
43
|
-
icon-symbol="link"
|
|
44
|
-
icon-size="medium"
|
|
45
|
-
icon-position="right"
|
|
46
|
-
aria-label={copyUrlButtonText}
|
|
47
|
-
onclick={handleCopyUrl}
|
|
48
|
-
>
|
|
49
|
-
{copyUrlButtonText}
|
|
50
|
-
</dx-button>
|
|
51
|
-
</dx-tooltip>
|
|
52
|
-
</div>
|
|
2
|
+
<dx-split-button
|
|
3
|
+
lwc:if={markdownUrl}
|
|
4
|
+
label="Copy as Markdown"
|
|
5
|
+
icon-symbol="copy"
|
|
6
|
+
icon-position="right"
|
|
7
|
+
variant="secondary"
|
|
8
|
+
size="small"
|
|
9
|
+
menu-aria-label="More content actions"
|
|
10
|
+
options={options}
|
|
11
|
+
stay-open-after-change
|
|
12
|
+
onactionclick={handleCopyMarkdown}
|
|
13
|
+
onselect={handleMenuSelect}
|
|
14
|
+
></dx-split-button>
|
|
53
15
|
</template>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LightningElement, api } from "lwc";
|
|
2
|
+
import { OptionWithNested } from "typings/custom";
|
|
2
3
|
import { track } from "dxUtils/analytics";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_COPY_TOOLTIP_LABEL = "Click to copy";
|
|
@@ -9,6 +10,22 @@ const ANALYTICS_CONTENT_CATEGORY = "content action toolbar";
|
|
|
9
10
|
const COPY_MARKDOWN_LABEL = "Copy as Markdown";
|
|
10
11
|
const VIEW_MARKDOWN_LABEL = "View as Markdown";
|
|
11
12
|
const COPY_URL_LABEL = "Copy URL to Markdown";
|
|
13
|
+
const VIEW_LLMS_LABEL = "View llms.txt";
|
|
14
|
+
const COPY_LLMS_URL_LABEL = "Copy URL to llms.txt";
|
|
15
|
+
|
|
16
|
+
// Option ids for the split-button menu; used to route `select` events.
|
|
17
|
+
const OPTION_COPY_MARKDOWN = "copy-markdown";
|
|
18
|
+
const OPTION_VIEW_MARKDOWN = "view-markdown";
|
|
19
|
+
const OPTION_COPY_MARKDOWN_URL = "copy-markdown-url";
|
|
20
|
+
const OPTION_VIEW_LLMS = "view-llms";
|
|
21
|
+
const OPTION_COPY_LLMS_URL = "copy-llms-url";
|
|
22
|
+
|
|
23
|
+
type ToolbarCtaLabel =
|
|
24
|
+
| typeof COPY_MARKDOWN_LABEL
|
|
25
|
+
| typeof VIEW_MARKDOWN_LABEL
|
|
26
|
+
| typeof COPY_URL_LABEL
|
|
27
|
+
| typeof VIEW_LLMS_LABEL
|
|
28
|
+
| typeof COPY_LLMS_URL_LABEL;
|
|
12
29
|
|
|
13
30
|
export default class ContentActionToolbar extends LightningElement {
|
|
14
31
|
@api
|
|
@@ -23,8 +40,9 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
23
40
|
}
|
|
24
41
|
private _pageUrl?: string;
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
// Id of the copy option currently flashing "Copied!" (null = none). Drives
|
|
44
|
+
// the per-option tooltip label; reassigning it re-renders `options`.
|
|
45
|
+
private copiedOptionId: string | null = null;
|
|
28
46
|
|
|
29
47
|
private copyTooltipResetTimeout: number | null = null;
|
|
30
48
|
|
|
@@ -50,16 +68,100 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
50
68
|
return url.toString();
|
|
51
69
|
}
|
|
52
70
|
|
|
53
|
-
|
|
54
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Site-root llms.txt file (same for every page), e.g.
|
|
73
|
+
* `https://example.com/llms.txt`.
|
|
74
|
+
*/
|
|
75
|
+
get llmsTxtUrl(): string {
|
|
76
|
+
return new URL("/docs/llms.txt", window.location.origin).toString();
|
|
55
77
|
}
|
|
56
78
|
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
// Hardcoded menu preset rendered by dx-split-button, grouped under bold
|
|
80
|
+
// subheaders with a divider between "This Page" and "All Documentation".
|
|
81
|
+
// Copy options carry a tooltip that flashes "Copied!" after a copy.
|
|
82
|
+
get options(): OptionWithNested[] {
|
|
83
|
+
return [
|
|
84
|
+
{ id: "this-page", label: "This Page", subheader: true },
|
|
85
|
+
{
|
|
86
|
+
id: OPTION_COPY_MARKDOWN,
|
|
87
|
+
label: COPY_MARKDOWN_LABEL,
|
|
88
|
+
tooltip: this.copyTooltipFor(OPTION_COPY_MARKDOWN),
|
|
89
|
+
rightIconData: {
|
|
90
|
+
iconSymbol: "copy",
|
|
91
|
+
iconSprite: "utility",
|
|
92
|
+
iconColor: "gray-50"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: OPTION_VIEW_MARKDOWN,
|
|
97
|
+
label: VIEW_MARKDOWN_LABEL,
|
|
98
|
+
rightIconData: {
|
|
99
|
+
iconSymbol: "new_window",
|
|
100
|
+
iconSprite: "utility",
|
|
101
|
+
iconColor: "gray-50"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: OPTION_COPY_MARKDOWN_URL,
|
|
106
|
+
label: COPY_URL_LABEL,
|
|
107
|
+
tooltip: this.copyTooltipFor(OPTION_COPY_MARKDOWN_URL),
|
|
108
|
+
rightIconData: {
|
|
109
|
+
iconSymbol: "link",
|
|
110
|
+
iconSprite: "utility",
|
|
111
|
+
iconColor: "gray-50"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{ id: "divider-1", label: "", divider: true },
|
|
115
|
+
{ id: "all-docs", label: "All Documentation", subheader: true },
|
|
116
|
+
{
|
|
117
|
+
id: OPTION_VIEW_LLMS,
|
|
118
|
+
label: VIEW_LLMS_LABEL,
|
|
119
|
+
rightIconData: {
|
|
120
|
+
iconSymbol: "new_window",
|
|
121
|
+
iconSprite: "utility",
|
|
122
|
+
iconColor: "gray-50"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: OPTION_COPY_LLMS_URL,
|
|
127
|
+
label: COPY_LLMS_URL_LABEL,
|
|
128
|
+
tooltip: this.copyTooltipFor(OPTION_COPY_LLMS_URL),
|
|
129
|
+
rightIconData: {
|
|
130
|
+
iconSymbol: "link",
|
|
131
|
+
iconSprite: "utility",
|
|
132
|
+
iconColor: "gray-50"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
];
|
|
59
136
|
}
|
|
60
137
|
|
|
61
|
-
|
|
62
|
-
|
|
138
|
+
// "Copied!" while this option is flashing, otherwise "Click to copy".
|
|
139
|
+
private copyTooltipFor(optionId: string): string {
|
|
140
|
+
return this.copiedOptionId === optionId
|
|
141
|
+
? COPIED_TOOLTIP_LABEL
|
|
142
|
+
: DEFAULT_COPY_TOOLTIP_LABEL;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Routes a menu selection to the matching action handler. */
|
|
146
|
+
handleMenuSelect(event: CustomEvent) {
|
|
147
|
+
switch (event.detail) {
|
|
148
|
+
case OPTION_COPY_MARKDOWN:
|
|
149
|
+
this.handleCopyMarkdown(event);
|
|
150
|
+
break;
|
|
151
|
+
case OPTION_VIEW_MARKDOWN:
|
|
152
|
+
this.handleViewMarkdown(event);
|
|
153
|
+
break;
|
|
154
|
+
case OPTION_COPY_MARKDOWN_URL:
|
|
155
|
+
this.handleCopyUrl(event);
|
|
156
|
+
break;
|
|
157
|
+
case OPTION_VIEW_LLMS:
|
|
158
|
+
this.handleViewLlms(event);
|
|
159
|
+
break;
|
|
160
|
+
case OPTION_COPY_LLMS_URL:
|
|
161
|
+
this.handleCopyLlmsUrl(event);
|
|
162
|
+
break;
|
|
163
|
+
default:
|
|
164
|
+
}
|
|
63
165
|
}
|
|
64
166
|
|
|
65
167
|
async handleCopyMarkdown(event: Event) {
|
|
@@ -81,7 +183,7 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
81
183
|
}
|
|
82
184
|
const markdown = await response.text();
|
|
83
185
|
await navigator.clipboard.writeText(markdown);
|
|
84
|
-
this.flashCopied(
|
|
186
|
+
this.flashCopied(OPTION_COPY_MARKDOWN);
|
|
85
187
|
} catch (error) {
|
|
86
188
|
console.error(error);
|
|
87
189
|
}
|
|
@@ -116,16 +218,55 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
116
218
|
|
|
117
219
|
try {
|
|
118
220
|
await navigator.clipboard.writeText(this.markdownUrl);
|
|
119
|
-
this.flashCopied(
|
|
221
|
+
this.flashCopied(OPTION_COPY_MARKDOWN_URL);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error(error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
handleViewLlms(event: Event) {
|
|
228
|
+
this.trackToolbarEvent(
|
|
229
|
+
event,
|
|
230
|
+
"custEv_linkClick",
|
|
231
|
+
VIEW_LLMS_LABEL,
|
|
232
|
+
this.llmsTxtUrl
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
window.open(this.llmsTxtUrl, "_blank", "noopener,noreferrer");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async handleCopyLlmsUrl(event: Event) {
|
|
239
|
+
this.trackToolbarEvent(
|
|
240
|
+
event,
|
|
241
|
+
"custEv_linkClick",
|
|
242
|
+
COPY_LLMS_URL_LABEL,
|
|
243
|
+
this.llmsTxtUrl
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
await navigator.clipboard.writeText(this.llmsTxtUrl);
|
|
248
|
+
this.flashCopied(OPTION_COPY_LLMS_URL);
|
|
120
249
|
} catch (error) {
|
|
121
250
|
console.error(error);
|
|
122
251
|
}
|
|
123
252
|
}
|
|
124
253
|
|
|
254
|
+
// Flash the given copy option's tooltip to "Copied!" then revert.
|
|
255
|
+
private flashCopied(optionId: string) {
|
|
256
|
+
if (this.copyTooltipResetTimeout !== null) {
|
|
257
|
+
window.clearTimeout(this.copyTooltipResetTimeout);
|
|
258
|
+
}
|
|
259
|
+
this.copiedOptionId = optionId;
|
|
260
|
+
this.copyTooltipResetTimeout = window.setTimeout(() => {
|
|
261
|
+
this.copiedOptionId = null;
|
|
262
|
+
this.copyTooltipResetTimeout = null;
|
|
263
|
+
}, COPIED_TOOLTIP_RESET_MS);
|
|
264
|
+
}
|
|
265
|
+
|
|
125
266
|
private trackToolbarEvent(
|
|
126
267
|
event: Event,
|
|
127
268
|
eventName: "custEv_linkClick",
|
|
128
|
-
label:
|
|
269
|
+
label: ToolbarCtaLabel,
|
|
129
270
|
url: string
|
|
130
271
|
): void {
|
|
131
272
|
track(event.currentTarget!, eventName, {
|
|
@@ -135,21 +276,9 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
135
276
|
element_title: label,
|
|
136
277
|
content_category: ANALYTICS_CONTENT_CATEGORY,
|
|
137
278
|
// Source page the CTA was clicked from; `click_url` above is the
|
|
138
|
-
//
|
|
279
|
+
// destination. Uses the same `page_path` expression as the
|
|
139
280
|
// amfReference doc_page_view events.
|
|
140
281
|
page_path: window.location.pathname + window.location.search
|
|
141
282
|
});
|
|
142
283
|
}
|
|
143
|
-
|
|
144
|
-
private flashCopied(labelKey: "copyMarkdownLabel" | "copyUrlLabel") {
|
|
145
|
-
if (this.copyTooltipResetTimeout !== null) {
|
|
146
|
-
window.clearTimeout(this.copyTooltipResetTimeout);
|
|
147
|
-
}
|
|
148
|
-
this[labelKey] = COPIED_TOOLTIP_LABEL;
|
|
149
|
-
this.copyTooltipResetTimeout = window.setTimeout(() => {
|
|
150
|
-
this.copyMarkdownLabel = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
151
|
-
this.copyUrlLabel = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
152
|
-
this.copyTooltipResetTimeout = null;
|
|
153
|
-
}, COPIED_TOOLTIP_RESET_MS);
|
|
154
|
-
}
|
|
155
284
|
}
|
|
@@ -10,8 +10,8 @@ export const DUMMY_MARKDOWN_CONTENT = `# Dummy Markdown
|
|
|
10
10
|
|
|
11
11
|
Storybook serves this placeholder content in place of the real markdown
|
|
12
12
|
that the docs backend would return for the current page. It exists so
|
|
13
|
-
the "
|
|
14
|
-
"Copy URL to Markdown" clipboard behavior are all exercisable in
|
|
13
|
+
the "Copy as Markdown" clipboard action, the "View as Markdown" new tab,
|
|
14
|
+
and the "Copy URL to Markdown" clipboard behavior are all exercisable in
|
|
15
15
|
storybook without hitting the live backend.
|
|
16
16
|
|
|
17
17
|
- Item 1
|
|
@@ -19,18 +19,30 @@ storybook without hitting the live backend.
|
|
|
19
19
|
- Item 3
|
|
20
20
|
`;
|
|
21
21
|
|
|
22
|
+
export const DUMMY_LLMS_CONTENT = `# Dummy llms.txt
|
|
23
|
+
|
|
24
|
+
Storybook serves this placeholder in place of the site-root llms.txt so
|
|
25
|
+
the "View llms.txt" new tab and "Copy URL to llms.txt" clipboard behavior
|
|
26
|
+
are exercisable without hitting the live backend.
|
|
27
|
+
`;
|
|
28
|
+
|
|
22
29
|
export const DUMMY_MARKDOWN_DATA_URL = `data:text/markdown;charset=utf-8,${encodeURIComponent(
|
|
23
30
|
DUMMY_MARKDOWN_CONTENT
|
|
24
31
|
)}`;
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
export const DUMMY_LLMS_DATA_URL = `data:text/plain;charset=utf-8,${encodeURIComponent(
|
|
34
|
+
DUMMY_LLMS_CONTENT
|
|
35
|
+
)}`;
|
|
36
|
+
|
|
37
|
+
/** Intercepts `.md` and `/llms.txt` GET requests and returns dummy content. */
|
|
27
38
|
export const contentActionToolbarMswHandlers = [
|
|
28
|
-
http.get(/\.md(\?.*)?$/, () => HttpResponse.text(DUMMY_MARKDOWN_CONTENT))
|
|
39
|
+
http.get(/\.md(\?.*)?$/, () => HttpResponse.text(DUMMY_MARKDOWN_CONTENT)),
|
|
40
|
+
http.get(/\/llms\.txt(\?.*)?$/, () => HttpResponse.text(DUMMY_LLMS_CONTENT))
|
|
29
41
|
];
|
|
30
42
|
|
|
31
43
|
let windowOpenIntercepted = false;
|
|
32
44
|
|
|
33
|
-
/** Redirects `window.open` calls for `.md` URLs to
|
|
45
|
+
/** Redirects `window.open` calls for `.md` and `llms.txt` URLs to dummy data URLs (MSW does not cover new tabs). */
|
|
34
46
|
export function interceptWindowOpenForContentActionToolbar() {
|
|
35
47
|
if (windowOpenIntercepted) {
|
|
36
48
|
return;
|
|
@@ -43,6 +55,9 @@ export function interceptWindowOpenForContentActionToolbar() {
|
|
|
43
55
|
if (stringUrl.endsWith(".md")) {
|
|
44
56
|
return originalOpen(DUMMY_MARKDOWN_DATA_URL, target, features);
|
|
45
57
|
}
|
|
58
|
+
if (stringUrl.endsWith("llms.txt")) {
|
|
59
|
+
return originalOpen(DUMMY_LLMS_DATA_URL, target, features);
|
|
60
|
+
}
|
|
46
61
|
return originalOpen(url ?? "", target, features);
|
|
47
62
|
}) as typeof window.open;
|
|
48
63
|
}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
@media screen and (max-width: 768px) {
|
|
2
|
-
:host {
|
|
3
|
-
display: block;
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
|
|
7
1
|
:host .survey-container {
|
|
8
2
|
align-items: center;
|
|
9
3
|
align-content: center;
|
|
@@ -13,18 +7,6 @@
|
|
|
13
7
|
margin: 20px 0;
|
|
14
8
|
}
|
|
15
9
|
|
|
16
|
-
/* when phone size, make the survey container a column */
|
|
17
|
-
@media screen and (max-width: 768px) {
|
|
18
|
-
:host .survey-container {
|
|
19
|
-
flex-direction: column;
|
|
20
|
-
gap: var(--dx-g-spacing-md);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.text-container {
|
|
24
|
-
text-align: center;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
10
|
dx-hr:first-of-type::part(hr) {
|
|
29
11
|
margin-top: var(--dx-g-spacing-2xl);
|
|
30
12
|
}
|