@veluai/velu 0.1.13 → 0.1.15

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.
Files changed (98) hide show
  1. package/README.md +80 -80
  2. package/dist/cli.js +21 -21
  3. package/package.json +3 -2
  4. package/runtime/velu-ui/components/Accordion.jsx +64 -64
  5. package/runtime/velu-ui/components/ApiClient.jsx +121 -121
  6. package/runtime/velu-ui/components/ApiField.jsx +87 -87
  7. package/runtime/velu-ui/components/ApiPath.jsx +63 -63
  8. package/runtime/velu-ui/components/ApiSidebar.jsx +122 -122
  9. package/runtime/velu-ui/components/AskBar.jsx +71 -71
  10. package/runtime/velu-ui/components/Callout.jsx +114 -114
  11. package/runtime/velu-ui/components/Card.jsx +131 -131
  12. package/runtime/velu-ui/components/Chatbot.jsx +596 -596
  13. package/runtime/velu-ui/components/CodeBlock.jsx +375 -375
  14. package/runtime/velu-ui/components/Columns.jsx +56 -56
  15. package/runtime/velu-ui/components/ErrorCard.jsx +138 -138
  16. package/runtime/velu-ui/components/Field.jsx +81 -81
  17. package/runtime/velu-ui/components/Image.jsx +163 -163
  18. package/runtime/velu-ui/components/Logo.jsx +31 -0
  19. package/runtime/velu-ui/components/MethodBadge.jsx +31 -31
  20. package/runtime/velu-ui/components/NavSelect.jsx +108 -108
  21. package/runtime/velu-ui/components/PageFeedback.jsx +219 -219
  22. package/runtime/velu-ui/components/PageFooter.jsx +145 -213
  23. package/runtime/velu-ui/components/PageHeader.jsx +422 -414
  24. package/runtime/velu-ui/components/PageNav.jsx +77 -77
  25. package/runtime/velu-ui/components/PoweredBy.jsx +51 -51
  26. package/runtime/velu-ui/components/Prompt.jsx +115 -115
  27. package/runtime/velu-ui/components/Search.jsx +411 -366
  28. package/runtime/velu-ui/components/Sidebar.jsx +191 -191
  29. package/runtime/velu-ui/components/SocialLinks.jsx +90 -0
  30. package/runtime/velu-ui/components/Steps.jsx +65 -65
  31. package/runtime/velu-ui/components/ThemeToggle.jsx +48 -48
  32. package/runtime/velu-ui/components/Toc.jsx +537 -537
  33. package/runtime/velu-ui/components/TocBar.jsx +195 -195
  34. package/runtime/velu-ui/components/Tree.jsx +87 -87
  35. package/runtime/velu-ui/components/TryItBar.jsx +90 -90
  36. package/runtime/velu-ui/components/accordion.css +92 -92
  37. package/runtime/velu-ui/components/api.css +479 -479
  38. package/runtime/velu-ui/components/ask-bar.css +94 -94
  39. package/runtime/velu-ui/components/card.css +105 -105
  40. package/runtime/velu-ui/components/chatbot.css +617 -617
  41. package/runtime/velu-ui/components/code-block.css +263 -263
  42. package/runtime/velu-ui/components/docs-layout.css +784 -775
  43. package/runtime/velu-ui/components/field.css +82 -82
  44. package/runtime/velu-ui/components/image.css +237 -237
  45. package/runtime/velu-ui/components/nav-select.css +157 -157
  46. package/runtime/velu-ui/components/page-feedback.css +241 -241
  47. package/runtime/velu-ui/components/page-footer.css +130 -130
  48. package/runtime/velu-ui/components/page-header.css +557 -520
  49. package/runtime/velu-ui/components/page-nav.css +50 -50
  50. package/runtime/velu-ui/components/powered-by.css +86 -66
  51. package/runtime/velu-ui/components/prompt.css +99 -99
  52. package/runtime/velu-ui/components/search.css +307 -307
  53. package/runtime/velu-ui/components/sidebar.css +144 -144
  54. package/runtime/velu-ui/components/steps.css +77 -77
  55. package/runtime/velu-ui/components/theme-toggle.css +101 -101
  56. package/runtime/velu-ui/components/toc-bar.css +234 -234
  57. package/runtime/velu-ui/components/tree.css +49 -49
  58. package/runtime/velu-ui/index.js +48 -46
  59. package/runtime/velu-ui/lib/component-schemas.js +100 -100
  60. package/runtime/velu-ui/lib/copyText.js +64 -64
  61. package/runtime/velu-ui/lib/lang-icons.jsx +147 -147
  62. package/runtime/velu-ui/lib/prism-langs.js +957 -957
  63. package/runtime/velu-ui/lib/prism-loader.js +74 -74
  64. package/runtime/velu-ui/lib/resolveIcon.jsx +29 -29
  65. package/runtime/velu-ui/lib/scrollIntoNearestView.js +66 -66
  66. package/runtime/velu-ui/mdx-components.jsx +105 -105
  67. package/runtime/velu-ui/primitives/Cluster.jsx +49 -49
  68. package/runtime/velu-ui/primitives/Stack.jsx +63 -63
  69. package/runtime/velu-ui/primitives/Switcher.jsx +57 -57
  70. package/runtime/velu-ui/primitives/stack.css +3 -3
  71. package/runtime/velu-ui/primitives/switcher.css +25 -25
  72. package/runtime/velu-ui/styles.css +43 -43
  73. package/runtime/velu-ui/tokens.css +4 -4
  74. package/schema/velu.schema.json +281 -167
  75. package/src/lib/extract-mdx-error.js +170 -170
  76. package/src/lib/issues.js +159 -159
  77. package/src/lib/known-components.js +34 -34
  78. package/src/navigation.js +434 -434
  79. package/src/runtime/App.jsx +1506 -1476
  80. package/src/runtime/ErrorBoundary.jsx +54 -54
  81. package/src/runtime/client-entry.jsx +22 -22
  82. package/src/runtime/server-entry.jsx +16 -16
  83. package/src/template.html +48 -48
  84. package/templates/starter/ai-tools/claude-code.mdx +26 -26
  85. package/templates/starter/ai-tools/cursor.mdx +17 -17
  86. package/templates/starter/api-reference/endpoint/create.mdx +24 -24
  87. package/templates/starter/api-reference/endpoint/get.mdx +27 -27
  88. package/templates/starter/api-reference/introduction.mdx +28 -28
  89. package/templates/starter/development.mdx +19 -19
  90. package/templates/starter/essentials/code.mdx +29 -29
  91. package/templates/starter/essentials/images.mdx +29 -29
  92. package/templates/starter/essentials/markdown.mdx +25 -25
  93. package/templates/starter/essentials/navigation.mdx +39 -39
  94. package/templates/starter/essentials/settings.mdx +30 -30
  95. package/templates/starter/favicon.svg +6 -6
  96. package/templates/starter/index.mdx +31 -31
  97. package/templates/starter/quickstart.mdx +31 -31
  98. package/templates/starter/velu.json +33 -33
@@ -1,46 +1,48 @@
1
- export { default as Stack } from './primitives/Stack.jsx';
2
- export { default as Switcher } from './primitives/Switcher.jsx';
3
- export { default as Cluster } from './primitives/Cluster.jsx';
4
- export { default as ThemeToggle } from './components/ThemeToggle.jsx';
5
- export { default as Sidebar } from './components/Sidebar.jsx';
6
- export { default as NavSelect } from './components/NavSelect.jsx';
7
- export { default as Toc } from './components/Toc.jsx';
8
- export { default as TocBar } from './components/TocBar.jsx';
9
- export { default as Callout } from './components/Callout.jsx';
10
- export { default as ErrorCard } from './components/ErrorCard.jsx';
11
- export {
12
- default as Accordion,
13
- AccordionGroup,
14
- } from './components/Accordion.jsx';
15
- export { default as Card, CardGroup } from './components/Card.jsx';
16
- export { default as Columns } from './components/Columns.jsx';
17
- export { default as Field } from './components/Field.jsx';
18
- export { default as Prompt } from './components/Prompt.jsx';
19
- export { default as Steps, Step } from './components/Steps.jsx';
20
- export { default as Tree, Folder, File } from './components/Tree.jsx';
21
- export { default as MethodBadge } from './components/MethodBadge.jsx';
22
- export { default as ApiPath } from './components/ApiPath.jsx';
23
- export { default as TryItBar } from './components/TryItBar.jsx';
24
- export { default as ApiField } from './components/ApiField.jsx';
25
- export { default as ApiClient } from './components/ApiClient.jsx';
26
- export { default as ApiSidebar } from './components/ApiSidebar.jsx';
27
- export { default as AskBar } from './components/AskBar.jsx';
28
- export { default as Chatbot } from './components/Chatbot.jsx';
29
- export { default as PageFeedback } from './components/PageFeedback.jsx';
30
- export { default as PageNav } from './components/PageNav.jsx';
31
- export { default as PageFooter } from './components/PageFooter.jsx';
32
- export { default as PoweredBy } from './components/PoweredBy.jsx';
33
- export { default as resolveIcon } from './lib/resolveIcon.jsx';
34
- export {
35
- default as PageHeader,
36
- VeluMark,
37
- } from './components/PageHeader.jsx';
38
- export { default as Search } from './components/Search.jsx';
39
- export { default as Image } from './components/Image.jsx';
40
- export {
41
- default as CodeBlock,
42
- CodeGroup,
43
- } from './components/CodeBlock.jsx';
44
- export {
45
- default as defaultMdxComponents,
46
- } from './mdx-components.jsx';
1
+ export { default as Stack } from './primitives/Stack.jsx';
2
+ export { default as Switcher } from './primitives/Switcher.jsx';
3
+ export { default as Cluster } from './primitives/Cluster.jsx';
4
+ export { default as ThemeToggle } from './components/ThemeToggle.jsx';
5
+ export { default as Sidebar } from './components/Sidebar.jsx';
6
+ export { default as NavSelect } from './components/NavSelect.jsx';
7
+ export { default as Toc } from './components/Toc.jsx';
8
+ export { default as TocBar } from './components/TocBar.jsx';
9
+ export { default as Callout } from './components/Callout.jsx';
10
+ export { default as ErrorCard } from './components/ErrorCard.jsx';
11
+ export { default as Logo } from './components/Logo.jsx';
12
+ export { default as SocialLinks } from './components/SocialLinks.jsx';
13
+ export {
14
+ default as Accordion,
15
+ AccordionGroup,
16
+ } from './components/Accordion.jsx';
17
+ export { default as Card, CardGroup } from './components/Card.jsx';
18
+ export { default as Columns } from './components/Columns.jsx';
19
+ export { default as Field } from './components/Field.jsx';
20
+ export { default as Prompt } from './components/Prompt.jsx';
21
+ export { default as Steps, Step } from './components/Steps.jsx';
22
+ export { default as Tree, Folder, File } from './components/Tree.jsx';
23
+ export { default as MethodBadge } from './components/MethodBadge.jsx';
24
+ export { default as ApiPath } from './components/ApiPath.jsx';
25
+ export { default as TryItBar } from './components/TryItBar.jsx';
26
+ export { default as ApiField } from './components/ApiField.jsx';
27
+ export { default as ApiClient } from './components/ApiClient.jsx';
28
+ export { default as ApiSidebar } from './components/ApiSidebar.jsx';
29
+ export { default as AskBar } from './components/AskBar.jsx';
30
+ export { default as Chatbot } from './components/Chatbot.jsx';
31
+ export { default as PageFeedback } from './components/PageFeedback.jsx';
32
+ export { default as PageNav } from './components/PageNav.jsx';
33
+ export { default as PageFooter } from './components/PageFooter.jsx';
34
+ export { default as PoweredBy } from './components/PoweredBy.jsx';
35
+ export { default as resolveIcon } from './lib/resolveIcon.jsx';
36
+ export {
37
+ default as PageHeader,
38
+ VeluMark,
39
+ } from './components/PageHeader.jsx';
40
+ export { default as Search } from './components/Search.jsx';
41
+ export { default as Image } from './components/Image.jsx';
42
+ export {
43
+ default as CodeBlock,
44
+ CodeGroup,
45
+ } from './components/CodeBlock.jsx';
46
+ export {
47
+ default as defaultMdxComponents,
48
+ } from './mdx-components.jsx';
@@ -1,100 +1,100 @@
1
- // Per-component option schemas — the props whose wrong value would otherwise
2
- // render incorrectly *silently*. Deliberately small and high-signal; extend a
3
- // component's entry here when it gains a new constrained option.
4
- //
5
- // Pure data + a pure validator (no React, no other imports) so BOTH sides can
6
- // use it: the live MDX registry (velu-ui/mdx-components.jsx) validates at
7
- // render time, and `velu validate` (velu-cli) imports this same module to
8
- // statically check literal attributes — one source of truth, no drift.
9
-
10
- // Canonical accepted values (must match the components):
11
- // Callout — VARIANTS keys; the component is CASE-SENSITIVE (VARIANTS[type]).
12
- // MethodBadge — METHODS; the component lower-cases the input first.
13
- export const CALLOUT_TYPES = ['note', 'warning', 'info', 'tip', 'check', 'danger', 'callout'];
14
- export const METHOD_NAMES = ['get', 'post', 'put', 'patch', 'delete'];
15
-
16
- // schema: { enums: { prop: { values, caseSensitive } }, required: [prop, …] }
17
- const SCHEMAS = {
18
- Callout: { enums: { type: { values: CALLOUT_TYPES, caseSensitive: true } } },
19
- MethodBadge: { enums: { method: { values: METHOD_NAMES, caseSensitive: false } } },
20
- ApiPath: { enums: { method: { values: METHOD_NAMES, caseSensitive: false } } },
21
- Image: { required: ['src'] },
22
- };
23
-
24
- // Smallest edit distance match within a set (for "did you mean"). Tiny inputs.
25
- function closest(value, values) {
26
- const v = String(value).toLowerCase();
27
- let best = null;
28
- let bestD = Infinity;
29
- for (const cand of values) {
30
- const a = v;
31
- const b = cand.toLowerCase();
32
- // inline Levenshtein
33
- const m = a.length;
34
- const n = b.length;
35
- const d = Array.from({ length: n + 1 }, (_, i) => i);
36
- for (let i = 1; i <= m; i++) {
37
- let prev = d[0];
38
- d[0] = i;
39
- for (let j = 1; j <= n; j++) {
40
- const tmp = d[j];
41
- d[j] = Math.min(d[j] + 1, d[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
42
- prev = tmp;
43
- }
44
- }
45
- if (d[n] < bestD) {
46
- bestD = d[n];
47
- best = cand;
48
- }
49
- }
50
- return bestD <= 3 ? best : null;
51
- }
52
-
53
- /**
54
- * Validate a component's props against its schema.
55
- * @param {string} name component tag name
56
- * @param {object} props props passed to it
57
- * @param {{ checkRequired?: boolean }} [opts] static callers pass
58
- * checkRequired:false because a prop supplied via a {expression}
59
- * isn't a literal they can see, so "missing required" would be a
60
- * false positive.
61
- * @returns an issue-shaped object (category 'invalid-props') or null if valid.
62
- * { category, title, detail, hint, suggestion }
63
- */
64
- export function validateProps(name, props = {}, opts = {}) {
65
- const { checkRequired = true } = opts;
66
- const schema = SCHEMAS[name];
67
- if (!schema) return null;
68
-
69
- for (const req of checkRequired ? schema.required || [] : []) {
70
- const v = props[req];
71
- if (v == null || v === '') {
72
- return {
73
- category: 'invalid-props',
74
- title: `<${name}> is missing the required "${req}" prop`,
75
- hint: `Add ${req}="…" to the component.`,
76
- };
77
- }
78
- }
79
-
80
- for (const [prop, rule] of Object.entries(schema.enums || {})) {
81
- if (!(prop in props) || props[prop] == null) continue;
82
- const raw = props[prop];
83
- const ok = rule.caseSensitive
84
- ? rule.values.includes(raw)
85
- : rule.values.map((s) => s.toLowerCase()).includes(String(raw).toLowerCase());
86
- if (!ok) {
87
- const guess = closest(raw, rule.values);
88
- return {
89
- category: 'invalid-props',
90
- title: `Invalid "${prop}" on <${name}>: "${raw}"`,
91
- detail: `Expected one of: ${rule.values.join(', ')}.`,
92
- hint: guess ? `Did you mean "${guess}"?` : '',
93
- };
94
- }
95
- }
96
-
97
- return null;
98
- }
99
-
100
- export default validateProps;
1
+ // Per-component option schemas — the props whose wrong value would otherwise
2
+ // render incorrectly *silently*. Deliberately small and high-signal; extend a
3
+ // component's entry here when it gains a new constrained option.
4
+ //
5
+ // Pure data + a pure validator (no React, no other imports) so BOTH sides can
6
+ // use it: the live MDX registry (velu-ui/mdx-components.jsx) validates at
7
+ // render time, and `velu validate` (velu-cli) imports this same module to
8
+ // statically check literal attributes — one source of truth, no drift.
9
+
10
+ // Canonical accepted values (must match the components):
11
+ // Callout — VARIANTS keys; the component is CASE-SENSITIVE (VARIANTS[type]).
12
+ // MethodBadge — METHODS; the component lower-cases the input first.
13
+ export const CALLOUT_TYPES = ['note', 'warning', 'info', 'tip', 'check', 'danger', 'callout'];
14
+ export const METHOD_NAMES = ['get', 'post', 'put', 'patch', 'delete'];
15
+
16
+ // schema: { enums: { prop: { values, caseSensitive } }, required: [prop, …] }
17
+ const SCHEMAS = {
18
+ Callout: { enums: { type: { values: CALLOUT_TYPES, caseSensitive: true } } },
19
+ MethodBadge: { enums: { method: { values: METHOD_NAMES, caseSensitive: false } } },
20
+ ApiPath: { enums: { method: { values: METHOD_NAMES, caseSensitive: false } } },
21
+ Image: { required: ['src'] },
22
+ };
23
+
24
+ // Smallest edit distance match within a set (for "did you mean"). Tiny inputs.
25
+ function closest(value, values) {
26
+ const v = String(value).toLowerCase();
27
+ let best = null;
28
+ let bestD = Infinity;
29
+ for (const cand of values) {
30
+ const a = v;
31
+ const b = cand.toLowerCase();
32
+ // inline Levenshtein
33
+ const m = a.length;
34
+ const n = b.length;
35
+ const d = Array.from({ length: n + 1 }, (_, i) => i);
36
+ for (let i = 1; i <= m; i++) {
37
+ let prev = d[0];
38
+ d[0] = i;
39
+ for (let j = 1; j <= n; j++) {
40
+ const tmp = d[j];
41
+ d[j] = Math.min(d[j] + 1, d[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));
42
+ prev = tmp;
43
+ }
44
+ }
45
+ if (d[n] < bestD) {
46
+ bestD = d[n];
47
+ best = cand;
48
+ }
49
+ }
50
+ return bestD <= 3 ? best : null;
51
+ }
52
+
53
+ /**
54
+ * Validate a component's props against its schema.
55
+ * @param {string} name component tag name
56
+ * @param {object} props props passed to it
57
+ * @param {{ checkRequired?: boolean }} [opts] static callers pass
58
+ * checkRequired:false because a prop supplied via a {expression}
59
+ * isn't a literal they can see, so "missing required" would be a
60
+ * false positive.
61
+ * @returns an issue-shaped object (category 'invalid-props') or null if valid.
62
+ * { category, title, detail, hint, suggestion }
63
+ */
64
+ export function validateProps(name, props = {}, opts = {}) {
65
+ const { checkRequired = true } = opts;
66
+ const schema = SCHEMAS[name];
67
+ if (!schema) return null;
68
+
69
+ for (const req of checkRequired ? schema.required || [] : []) {
70
+ const v = props[req];
71
+ if (v == null || v === '') {
72
+ return {
73
+ category: 'invalid-props',
74
+ title: `<${name}> is missing the required "${req}" prop`,
75
+ hint: `Add ${req}="…" to the component.`,
76
+ };
77
+ }
78
+ }
79
+
80
+ for (const [prop, rule] of Object.entries(schema.enums || {})) {
81
+ if (!(prop in props) || props[prop] == null) continue;
82
+ const raw = props[prop];
83
+ const ok = rule.caseSensitive
84
+ ? rule.values.includes(raw)
85
+ : rule.values.map((s) => s.toLowerCase()).includes(String(raw).toLowerCase());
86
+ if (!ok) {
87
+ const guess = closest(raw, rule.values);
88
+ return {
89
+ category: 'invalid-props',
90
+ title: `Invalid "${prop}" on <${name}>: "${raw}"`,
91
+ detail: `Expected one of: ${rule.values.join(', ')}.`,
92
+ hint: guess ? `Did you mean "${guess}"?` : '',
93
+ };
94
+ }
95
+ }
96
+
97
+ return null;
98
+ }
99
+
100
+ export default validateProps;
@@ -1,64 +1,64 @@
1
- /**
2
- * copyText — copy a string to the clipboard, working in BOTH secure and
3
- * insecure contexts.
4
- *
5
- * `navigator.clipboard` only exists in a secure context (HTTPS or
6
- * localhost). On a plain-HTTP origin — e.g. previewing the dev server
7
- * over the LAN at http://<ip>:8358 on a phone — it's `undefined`, so a
8
- * `if (!navigator.clipboard) return` guard makes copy buttons silently
9
- * no-op. This util falls back to the legacy `execCommand('copy')` via a
10
- * hidden textarea so copy still works on those origins.
11
- *
12
- * Must be called from a user gesture (click) — both paths require it.
13
- * Returns a Promise: resolves on success, rejects on failure.
14
- */
15
- export default function copyText(text) {
16
- const str = String(text ?? '');
17
-
18
- // Preferred path — async Clipboard API (secure contexts only).
19
- if (
20
- typeof navigator !== 'undefined' &&
21
- navigator.clipboard &&
22
- typeof window !== 'undefined' &&
23
- window.isSecureContext
24
- ) {
25
- return navigator.clipboard.writeText(str);
26
- }
27
-
28
- // Fallback — hidden <textarea> + execCommand, for insecure origins.
29
- return new Promise((resolve, reject) => {
30
- if (typeof document === 'undefined') {
31
- reject(new Error('copyText: no document'));
32
- return;
33
- }
34
- const ta = document.createElement('textarea');
35
- ta.value = str;
36
- ta.setAttribute('readonly', '');
37
- // Keep it out of view and inert, but still selectable.
38
- ta.style.position = 'fixed';
39
- ta.style.top = '0';
40
- ta.style.left = '0';
41
- ta.style.inlineSize = '1px';
42
- ta.style.blockSize = '1px';
43
- ta.style.padding = '0';
44
- ta.style.border = 'none';
45
- ta.style.opacity = '0';
46
- document.body.appendChild(ta);
47
- ta.focus();
48
- ta.select();
49
- try {
50
- ta.setSelectionRange(0, str.length);
51
- } catch {
52
- /* some browsers reject setSelectionRange on a readonly field */
53
- }
54
- let ok = false;
55
- try {
56
- ok = document.execCommand('copy');
57
- } catch {
58
- ok = false;
59
- }
60
- document.body.removeChild(ta);
61
- if (ok) resolve();
62
- else reject(new Error('copyText: execCommand copy failed'));
63
- });
64
- }
1
+ /**
2
+ * copyText — copy a string to the clipboard, working in BOTH secure and
3
+ * insecure contexts.
4
+ *
5
+ * `navigator.clipboard` only exists in a secure context (HTTPS or
6
+ * localhost). On a plain-HTTP origin — e.g. previewing the dev server
7
+ * over the LAN at http://<ip>:8358 on a phone — it's `undefined`, so a
8
+ * `if (!navigator.clipboard) return` guard makes copy buttons silently
9
+ * no-op. This util falls back to the legacy `execCommand('copy')` via a
10
+ * hidden textarea so copy still works on those origins.
11
+ *
12
+ * Must be called from a user gesture (click) — both paths require it.
13
+ * Returns a Promise: resolves on success, rejects on failure.
14
+ */
15
+ export default function copyText(text) {
16
+ const str = String(text ?? '');
17
+
18
+ // Preferred path — async Clipboard API (secure contexts only).
19
+ if (
20
+ typeof navigator !== 'undefined' &&
21
+ navigator.clipboard &&
22
+ typeof window !== 'undefined' &&
23
+ window.isSecureContext
24
+ ) {
25
+ return navigator.clipboard.writeText(str);
26
+ }
27
+
28
+ // Fallback — hidden <textarea> + execCommand, for insecure origins.
29
+ return new Promise((resolve, reject) => {
30
+ if (typeof document === 'undefined') {
31
+ reject(new Error('copyText: no document'));
32
+ return;
33
+ }
34
+ const ta = document.createElement('textarea');
35
+ ta.value = str;
36
+ ta.setAttribute('readonly', '');
37
+ // Keep it out of view and inert, but still selectable.
38
+ ta.style.position = 'fixed';
39
+ ta.style.top = '0';
40
+ ta.style.left = '0';
41
+ ta.style.inlineSize = '1px';
42
+ ta.style.blockSize = '1px';
43
+ ta.style.padding = '0';
44
+ ta.style.border = 'none';
45
+ ta.style.opacity = '0';
46
+ document.body.appendChild(ta);
47
+ ta.focus();
48
+ ta.select();
49
+ try {
50
+ ta.setSelectionRange(0, str.length);
51
+ } catch {
52
+ /* some browsers reject setSelectionRange on a readonly field */
53
+ }
54
+ let ok = false;
55
+ try {
56
+ ok = document.execCommand('copy');
57
+ } catch {
58
+ ok = false;
59
+ }
60
+ document.body.removeChild(ta);
61
+ if (ok) resolve();
62
+ else reject(new Error('copyText: execCommand copy failed'));
63
+ });
64
+ }