@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/docs-components",
3
- "version": "1.33.2-alpha-iphone-scroll-7",
3
+ "version": "1.33.2-llms-txt-2",
4
4
  "description": "Docs Lightning web components for DSC",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
@@ -1,31 +1,24 @@
1
1
  @import "dxHelpers/reset";
2
2
 
3
3
  :host {
4
- display: block;
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
- .toolbar {
8
- display: flex;
9
- align-items: center;
10
- gap: var(--dx-g-spacing-smd);
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
- .toolbar-button {
15
- --dx-c-button-font-weight: var(--dx-g-font-demi);
16
- --dx-c-button-line-height: var(--dx-g-spacing-mlg);
17
- --dx-c-button-letter-spacing: 0.005em;
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
- .divider {
21
- width: 1px;
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
- <div class="toolbar" lwc:if={markdownUrl}>
3
- <dx-tooltip placement="top-right" label={copyMarkdownLabel}>
4
- <dx-button
5
- class="toolbar-button"
6
- variant="inline"
7
- size="small"
8
- icon-sprite="utility"
9
- icon-symbol="copy"
10
- icon-size="medium"
11
- icon-position="right"
12
- aria-label={copyMarkdownButtonText}
13
- onclick={handleCopyMarkdown}
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
- copyMarkdownLabel: string = DEFAULT_COPY_TOOLTIP_LABEL;
27
- copyUrlLabel: string = DEFAULT_COPY_TOOLTIP_LABEL;
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
- get copyMarkdownButtonText(): string {
54
- return COPY_MARKDOWN_LABEL;
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
- get viewMarkdownButtonText(): string {
58
- return VIEW_MARKDOWN_LABEL;
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
- get copyUrlButtonText(): string {
62
- return COPY_URL_LABEL;
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("copyMarkdownLabel");
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("copyUrlLabel");
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: string,
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
- // `.md` destination. Uses the same `page_path` expression as the
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 "Copied" tooltip, the "View as Markdown" new tab, and 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
- /** Intercepts any `.md` GET request and returns the dummy markdown. */
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 the dummy markdown data URL (MSW does not cover new tabs). */
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
  }