hyperclayjs 1.0.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/LICENSE +21 -0
- package/README.md +360 -0
- package/README.template.md +276 -0
- package/communication/behaviorCollector.js +230 -0
- package/communication/sendMessage.js +48 -0
- package/communication/uploadFile.js +348 -0
- package/core/adminContenteditable.js +36 -0
- package/core/adminInputs.js +58 -0
- package/core/adminOnClick.js +31 -0
- package/core/adminResources.js +33 -0
- package/core/adminSystem.js +15 -0
- package/core/editmode.js +8 -0
- package/core/editmodeSystem.js +18 -0
- package/core/enablePersistentFormInputValues.js +62 -0
- package/core/isAdminOfCurrentResource.js +13 -0
- package/core/optionVisibilityRuleGenerator.js +160 -0
- package/core/savePage.js +196 -0
- package/core/savePageCore.js +236 -0
- package/core/setPageTypeOnDocumentElement.js +23 -0
- package/custom-attributes/ajaxElements.js +94 -0
- package/custom-attributes/autosize.js +17 -0
- package/custom-attributes/domHelpers.js +175 -0
- package/custom-attributes/events.js +15 -0
- package/custom-attributes/inputHelpers.js +11 -0
- package/custom-attributes/onclickaway.js +27 -0
- package/custom-attributes/onclone.js +35 -0
- package/custom-attributes/onpagemutation.js +20 -0
- package/custom-attributes/onrender.js +30 -0
- package/custom-attributes/preventEnter.js +13 -0
- package/custom-attributes/sortable.js +76 -0
- package/dom-utilities/All.js +412 -0
- package/dom-utilities/getDataFromForm.js +60 -0
- package/dom-utilities/insertStyleTag.js +28 -0
- package/dom-utilities/onDomReady.js +7 -0
- package/dom-utilities/onLoad.js +7 -0
- package/hyperclay.js +465 -0
- package/module-dependency-graph.json +612 -0
- package/package.json +95 -0
- package/string-utilities/copy-to-clipboard.js +35 -0
- package/string-utilities/emmet-html.js +54 -0
- package/string-utilities/query.js +1 -0
- package/string-utilities/slugify.js +21 -0
- package/ui/info.js +39 -0
- package/ui/prompts.js +179 -0
- package/ui/theModal.js +677 -0
- package/ui/toast.js +273 -0
- package/utilities/cookie.js +45 -0
- package/utilities/debounce.js +12 -0
- package/utilities/mutation.js +403 -0
- package/utilities/nearest.js +97 -0
- package/utilities/pipe.js +1 -0
- package/utilities/throttle.js +21 -0
- package/vendor/Sortable.js +3351 -0
- package/vendor/idiomorph.min.js +8 -0
- package/vendor/tailwind-base.css +1471 -0
- package/vendor/tailwind-play.js +169 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function copyToClipboard(text) {
|
|
2
|
+
// Create temporary textarea element
|
|
3
|
+
const textarea = document.createElement('textarea');
|
|
4
|
+
textarea.value = text;
|
|
5
|
+
textarea.style.position = 'fixed';
|
|
6
|
+
textarea.style.opacity = '0';
|
|
7
|
+
|
|
8
|
+
// Add to DOM, select, copy, and remove
|
|
9
|
+
document.body.appendChild(textarea);
|
|
10
|
+
textarea.select();
|
|
11
|
+
textarea.setSelectionRange(0, 99999); // For mobile devices
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const successful = document.execCommand('copy');
|
|
15
|
+
if (successful) {
|
|
16
|
+
console.log('Copied to clipboard');
|
|
17
|
+
} else {
|
|
18
|
+
console.log('Copy failed');
|
|
19
|
+
}
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error('Unable to copy:', err);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
document.body.removeChild(textarea);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Export to window for global access
|
|
28
|
+
export function exportToWindow() {
|
|
29
|
+
if (!window.hyperclay) {
|
|
30
|
+
window.hyperclay = {};
|
|
31
|
+
}
|
|
32
|
+
window.hyperclay.copyToClipboard = copyToClipboard;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default copyToClipboard;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export default function emmet(strings, ...values) {
|
|
2
|
+
// Extract the Emmet syntax from the first string
|
|
3
|
+
let emmetSyntax = strings[0];
|
|
4
|
+
|
|
5
|
+
// Reconstruct the content from the rest of the strings and values
|
|
6
|
+
let content = '';
|
|
7
|
+
for (let i = 1; i < strings.length; i++) {
|
|
8
|
+
content += values[i - 1];
|
|
9
|
+
content += strings[i];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Parse the tag name
|
|
13
|
+
let tagMatch = emmetSyntax.match(/^([a-zA-Z][a-zA-Z0-9]*)/);
|
|
14
|
+
let tagName = tagMatch ? tagMatch[1] : 'div'; // Default to 'div' if no tag name is provided
|
|
15
|
+
|
|
16
|
+
// Parse class names
|
|
17
|
+
let classRegex = /\.([a-zA-Z0-9_-]+)/g;
|
|
18
|
+
let classNames = [];
|
|
19
|
+
let classMatch;
|
|
20
|
+
while ((classMatch = classRegex.exec(emmetSyntax)) !== null) {
|
|
21
|
+
classNames.push(classMatch[1]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Parse attributes
|
|
25
|
+
let attrRegex = /\[([^\]=]+)=([^\]]+)\]/g;
|
|
26
|
+
let attributes = {};
|
|
27
|
+
let attrMatch;
|
|
28
|
+
while ((attrMatch = attrRegex.exec(emmetSyntax)) !== null) {
|
|
29
|
+
attributes[attrMatch[1]] = attrMatch[2];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Build the opening tag
|
|
33
|
+
let html = `<${tagName}`;
|
|
34
|
+
|
|
35
|
+
// Add classes if any
|
|
36
|
+
if (classNames.length > 0) {
|
|
37
|
+
html += ` class="${classNames.join(' ')}"`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Add attributes if any
|
|
41
|
+
for (let attr in attributes) {
|
|
42
|
+
html += ` ${attr}="${attributes[attr]}"`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
html += '>';
|
|
46
|
+
|
|
47
|
+
// Add the content
|
|
48
|
+
html += content;
|
|
49
|
+
|
|
50
|
+
// Close the tag
|
|
51
|
+
html += `</${tagName}>`;
|
|
52
|
+
|
|
53
|
+
return html;
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default Object.fromEntries(new URLSearchParams(window.location.search));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// e.g. "Hello there" → "hello-there"
|
|
2
|
+
function slugify (text) {
|
|
3
|
+
return text.toString().toLowerCase()
|
|
4
|
+
.normalize('NFD') // separate accents from letters
|
|
5
|
+
.replace(/[\u0300-\u036f]/g, '') // remove accents
|
|
6
|
+
.replace(/\s+/g, '-') // replace spaces with -
|
|
7
|
+
.replace(/[^\w\-]+/g, '') // remove all non-word chars
|
|
8
|
+
.replace(/\-\-+/g, '-') // replace multiple - with single -
|
|
9
|
+
.replace(/^-+/, '') // trim - from start of text
|
|
10
|
+
.replace(/-+$/, ''); // trim - from end of text
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Export to window for global access
|
|
14
|
+
export function exportToWindow() {
|
|
15
|
+
if (!window.hyperclay) {
|
|
16
|
+
window.hyperclay = {};
|
|
17
|
+
}
|
|
18
|
+
window.hyperclay.slugify = slugify;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default slugify;
|
package/ui/info.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import themodal from "./theModal.js";
|
|
2
|
+
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
+
|
|
4
|
+
const CLOSE_BUTTON_SVG = `<svg class="group" viewBox="0 0 134 134" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="fill-[#1D2032] group-hover:fill-[#212543]" d="M132 132.5 1 1.5h131v131Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h3v1.5h1.5V3H6v1.5h1.5V6H9v1.5h1.5V9H12v1.5h1.5V12H15v1.5h1.5V15H18v1.5h1.5V18H21v1.5h1.5V21H24v1.5h1.5V24H27v1.5h1.5V27H30v1.5h1.5V30H33v1.5h1.5V33H36v1.5h1.5V36H39v1.5h1.5V39H42v1.5h1.5V42H45v1.5h1.5V45H48v1.5h1.5V48H51v1.5h1.5V51H54v1.5h1.5V54H57v1.5h1.5V57H60v1.5h1.5V60H63v1.5h1.5V63H66v1.5h1.5V66H69v1.5h1.5V69H72v1.5h1.5V72H75v1.5h1.5V75H78v1.5h1.5V78H81v1.5h1.5V81H84v1.5h1.5V84H87v1.5h1.5V87H90v1.5h1.5V90H93v1.5h1.5V93H96v1.5h1.5V96H99v1.5h1.5V99h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v3h-3V132H129v-1.5h-1.5V129H126v-1.5h-1.5V126H123v-1.5h-1.5V123H120v-1.5h-1.5V120H117v-1.5h-1.5V117H114v-1.5h-1.5V114H111v-1.5h-1.5V111H108v-1.5h-1.5V108H105v-1.5h-1.5V105H102v-1.5h-1.5V102H99v-1.5h-1.5V99H96v-1.5h-1.5V96H93v-1.5h-1.5V93H90v-1.5h-1.5V90H87v-1.5h-1.5V87H84v-1.5h-1.5V84H81v-1.5h-1.5V81H78v-1.5h-1.5V78H75v-1.5h-1.5V75H72v-1.5h-1.5V72H69v-1.5h-1.5V69H66v-1.5h-1.5V66H63v-1.5h-1.5V63H60v-1.5h-1.5V60H57v-1.5h-1.5V57H54v-1.5h-1.5V54H51v-1.5h-1.5V51H48v-1.5h-1.5V48H45v-1.5h-1.5V45H42v-1.5h-1.5V42H39v-1.5h-1.5V39H36v-1.5h-1.5V36H33v-1.5h-1.5V33H30v-1.5h-1.5V30H27v-1.5h-1.5V27H24v-1.5h-1.5V24H21v-1.5h-1.5V21H18v-1.5h-1.5V18H15v-1.5h-1.5V15H12v-1.5h-1.5V12H9v-1.5H7.5V9H6V7.5H4.5V6H3V4.5H1.5V3H0V0ZM108.8 22h5.2v5.1h-2.6v2.6H109v2.6h-2.6v2.6h-2.6v2.5h-2.6v5.2h2.6V45h2.6v2.6h2.6v2.6h2.5v2.6h2.6V58h-5.1v-2.6h-2.6V53h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6h-5.2v2.6H91v2.6h-2.6v2.6h-2.6v2.5h-2.6V58H78v-5.1h2.6v-2.6H83v-2.6h2.6v-2.6h2.6v-2.5h2.6v-5.2h-2.6V35h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6H78V22h5.2v2.6h2.5V27h2.6v2.6h2.6v2.6h2.5v2.6h5.2v-2.6h2.5v-2.6h2.6v-2.6h2.6v-2.5h2.5V22Z" fill="#fff"/></svg>`;
|
|
5
|
+
const CONFIRM_BUTTON_SVG = `<div style="width: 28px;"><svg viewBox="0 0 60 33" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.5 0.75H43.5V5.25H49V9.75H54.5V14.25H60V18.75H54.5V23.25H49V27.75H43.5V32.25H34.5V27.75H40V23.25H45.5V18.75H0V14.25H45.5V9.75H40V5.25H34.5V0.75Z" fill="white"/></svg></div>`;
|
|
6
|
+
|
|
7
|
+
export function info (promptText, ...content) {
|
|
8
|
+
themodal.html = `<div class="max-w-[440px] space-y-5 mb-7">
|
|
9
|
+
<div class="text-[20px] sm:text-[22px] font-bold">${promptText}</div>
|
|
10
|
+
${content.map(c => `<div class="text-[16px] sm:text-[18px] font-normal">${c}</div>`).join("")}
|
|
11
|
+
</div>`;
|
|
12
|
+
themodal.closeHtml = CLOSE_BUTTON_SVG;
|
|
13
|
+
themodal.yes = CONFIRM_BUTTON_SVG;
|
|
14
|
+
|
|
15
|
+
const promise = new Promise((resolve, reject) => {
|
|
16
|
+
themodal.onYes(() => {
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
themodal.onNo = () => {
|
|
21
|
+
reject();
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
themodal.open();
|
|
26
|
+
|
|
27
|
+
return promise;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Auto-initialize - cleanup any leftover modal elements
|
|
31
|
+
export function init() {
|
|
32
|
+
onDomReady(() => {
|
|
33
|
+
const micromodalParentElem = document.querySelector(".micromodal-parent");
|
|
34
|
+
if (micromodalParentElem) {
|
|
35
|
+
micromodalParentElem.remove();
|
|
36
|
+
document.body.style.overflow = "";
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
package/ui/prompts.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import themodal from "./theModal.js";
|
|
2
|
+
import onDomReady from "../dom-utilities/onDomReady.js";
|
|
3
|
+
import toast from "./toast.js";
|
|
4
|
+
import copyToClipboard from "../string-utilities/copy-to-clipboard.js";
|
|
5
|
+
|
|
6
|
+
const CLOSE_BUTTON_SVG = `<svg class="group" viewBox="0 0 134 134" fill="none" xmlns="http://www.w3.org/2000/svg"><path class="fill-[#1D2032] group-hover:fill-[#212543]" d="M132 132.5 1 1.5h131v131Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h3v1.5h1.5V3H6v1.5h1.5V6H9v1.5h1.5V9H12v1.5h1.5V12H15v1.5h1.5V15H18v1.5h1.5V18H21v1.5h1.5V21H24v1.5h1.5V24H27v1.5h1.5V27H30v1.5h1.5V30H33v1.5h1.5V33H36v1.5h1.5V36H39v1.5h1.5V39H42v1.5h1.5V42H45v1.5h1.5V45H48v1.5h1.5V48H51v1.5h1.5V51H54v1.5h1.5V54H57v1.5h1.5V57H60v1.5h1.5V60H63v1.5h1.5V63H66v1.5h1.5V66H69v1.5h1.5V69H72v1.5h1.5V72H75v1.5h1.5V75H78v1.5h1.5V78H81v1.5h1.5V81H84v1.5h1.5V84H87v1.5h1.5V87H90v1.5h1.5V90H93v1.5h1.5V93H96v1.5h1.5V96H99v1.5h1.5V99h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v1.5h1.5v3h-3V132H129v-1.5h-1.5V129H126v-1.5h-1.5V126H123v-1.5h-1.5V123H120v-1.5h-1.5V120H117v-1.5h-1.5V117H114v-1.5h-1.5V114H111v-1.5h-1.5V111H108v-1.5h-1.5V108H105v-1.5h-1.5V105H102v-1.5h-1.5V102H99v-1.5h-1.5V99H96v-1.5h-1.5V96H93v-1.5h-1.5V93H90v-1.5h-1.5V90H87v-1.5h-1.5V87H84v-1.5h-1.5V84H81v-1.5h-1.5V81H78v-1.5h-1.5V78H75v-1.5h-1.5V75H72v-1.5h-1.5V72H69v-1.5h-1.5V69H66v-1.5h-1.5V66H63v-1.5h-1.5V63H60v-1.5h-1.5V60H57v-1.5h-1.5V57H54v-1.5h-1.5V54H51v-1.5h-1.5V51H48v-1.5h-1.5V48H45v-1.5h-1.5V45H42v-1.5h-1.5V42H39v-1.5h-1.5V39H36v-1.5h-1.5V36H33v-1.5h-1.5V33H30v-1.5h-1.5V30H27v-1.5h-1.5V27H24v-1.5h-1.5V24H21v-1.5h-1.5V21H18v-1.5h-1.5V18H15v-1.5h-1.5V15H12v-1.5h-1.5V12H9v-1.5H7.5V9H6V7.5H4.5V6H3V4.5H1.5V3H0V0ZM108.8 22h5.2v5.1h-2.6v2.6H109v2.6h-2.6v2.6h-2.6v2.5h-2.6v5.2h2.6V45h2.6v2.6h2.6v2.6h2.5v2.6h2.6V58h-5.1v-2.6h-2.6V53h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6h-5.2v2.6H91v2.6h-2.6v2.6h-2.6v2.5h-2.6V58H78v-5.1h2.6v-2.6H83v-2.6h2.6v-2.6h2.6v-2.5h2.6v-5.2h-2.6V35h-2.6v-2.6h-2.6v-2.6h-2.5v-2.6H78V22h5.2v2.6h2.5V27h2.6v2.6h2.6v2.6h2.5v2.6h5.2v-2.6h2.5v-2.6h2.6v-2.6h2.6v-2.5h2.5V22Z" fill="#fff"/></svg>`;
|
|
7
|
+
const CONFIRM_BUTTON_SVG = `<div style="width: 28px;"><svg viewBox="0 0 60 33" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M34.5 0.75H43.5V5.25H49V9.75H54.5V14.25H60V18.75H54.5V23.25H49V27.75H43.5V32.25H34.5V27.75H40V23.25H45.5V18.75H0V14.25H45.5V9.75H40V5.25H34.5V0.75Z" fill="white"/></svg></div>`;
|
|
8
|
+
|
|
9
|
+
function createModal(promptText, yesCallback, extraContent = "", includeInput = false, defaultValue = "") {
|
|
10
|
+
const inputHtml = includeInput
|
|
11
|
+
? `<div><input class="micromodal__input" type="text" value="${defaultValue}" required></div>`
|
|
12
|
+
: "";
|
|
13
|
+
|
|
14
|
+
themodal.html = `<div>
|
|
15
|
+
<div class="micromodal__heading">${promptText}</div>
|
|
16
|
+
${inputHtml}
|
|
17
|
+
${extraContent}
|
|
18
|
+
</div>`;
|
|
19
|
+
themodal.closeHtml = CLOSE_BUTTON_SVG;
|
|
20
|
+
themodal.yes = CONFIRM_BUTTON_SVG;
|
|
21
|
+
|
|
22
|
+
const promise = new Promise((resolve, reject) => {
|
|
23
|
+
themodal.onYes(() => {
|
|
24
|
+
try {
|
|
25
|
+
if (includeInput) {
|
|
26
|
+
const promptResult = document.querySelector(".micromodal__input").value;
|
|
27
|
+
if (promptResult) {
|
|
28
|
+
if (yesCallback) yesCallback(promptResult);
|
|
29
|
+
resolve(promptResult);
|
|
30
|
+
return true; // Allow modal to close
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
if (yesCallback) yesCallback();
|
|
34
|
+
resolve();
|
|
35
|
+
return true; // Allow modal to close
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// Show error message as toast
|
|
39
|
+
toast(error.message || 'An error occurred', 'error');
|
|
40
|
+
// Keep modal open - don't reject or resolve
|
|
41
|
+
// User can try again
|
|
42
|
+
return false; // Prevent modal from closing
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
themodal.onNo = () => {
|
|
47
|
+
reject();
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
themodal.open();
|
|
52
|
+
|
|
53
|
+
return promise;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Public API functions
|
|
57
|
+
export function ask(promptText, yesCallback, defaultValue = "", extraContent = "") {
|
|
58
|
+
return createModal(promptText, yesCallback, extraContent, true, defaultValue);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function consent(promptText, yesCallback, extraContent = "") {
|
|
62
|
+
return createModal(promptText, yesCallback, extraContent, false);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function tell(promptText, yesCallback, extraContent = "") {
|
|
66
|
+
return createModal(promptText, yesCallback, extraContent, false);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Display a modal with a code snippet and copy functionality
|
|
71
|
+
* Following the existing modal pattern from prompts.js
|
|
72
|
+
*/
|
|
73
|
+
export function snippet(title, content, options = {}) {
|
|
74
|
+
const {
|
|
75
|
+
extraContent = 'Save this, it won\'t be shown again. Expires in 1 year.'
|
|
76
|
+
} = options;
|
|
77
|
+
|
|
78
|
+
// Create the modal content with copy button
|
|
79
|
+
const modalContent = `
|
|
80
|
+
<div class="bg-[#292E54] p-4 mb-[14px] max-w-[420px]">
|
|
81
|
+
<div class="overflow-x-auto">
|
|
82
|
+
<pre class="text-white font-mono text-sm whitespace-nowrap">${content}</pre>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<button type="button" class="custom-button group font-fixedsys text-center cursor-pointer border-[3px] border-t-[#474C65] border-r-[#131725] border-b-[#131725] border-l-[#474C65] bg-[#1D1F2F] hover:bg-[#232639] active:border-b-[#474C65] active:border-l-[#131725] active:border-t-[#131725] active:border-r-[#474C65] text-[23px] p-[2px_16px_4px] w-full mb-4 copy-snippet-btn">
|
|
87
|
+
<span class="whitespace-nowrap select-none inline-block group-active:translate-x-[1.5px] group-active:translate-y-[1.5px]">copy</span>
|
|
88
|
+
</button>
|
|
89
|
+
|
|
90
|
+
${extraContent ? `
|
|
91
|
+
<div class="p-3 border-2 border-[#989742] bg-[#1E1E11] text-sm text-[#FBF7B7] max-w-[420px]">
|
|
92
|
+
${extraContent}
|
|
93
|
+
</div>
|
|
94
|
+
` : ''}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
// Use the existing modal system
|
|
98
|
+
themodal.html = `<div>
|
|
99
|
+
<div class="micromodal__heading">${title}</div>
|
|
100
|
+
${modalContent}
|
|
101
|
+
</div>`;
|
|
102
|
+
|
|
103
|
+
themodal.closeHtml = CLOSE_BUTTON_SVG;
|
|
104
|
+
themodal.yes = CONFIRM_BUTTON_SVG;
|
|
105
|
+
|
|
106
|
+
const promise = new Promise((resolve) => {
|
|
107
|
+
// Local copy function
|
|
108
|
+
const handleCopy = function(event) {
|
|
109
|
+
if (event.target.closest('.copy-snippet-btn')) {
|
|
110
|
+
copyToClipboard(content);
|
|
111
|
+
toast('Copied to clipboard!', 'success');
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Add event listener to the modal container after it opens
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
const modalContainer = document.querySelector('.micromodal-parent');
|
|
118
|
+
if (modalContainer) {
|
|
119
|
+
modalContainer.addEventListener('click', handleCopy);
|
|
120
|
+
}
|
|
121
|
+
}, 0);
|
|
122
|
+
|
|
123
|
+
themodal.onYes(() => {
|
|
124
|
+
// Clean up the event listener
|
|
125
|
+
const modalContainer = document.querySelector('.micromodal-parent');
|
|
126
|
+
if (modalContainer) {
|
|
127
|
+
modalContainer.removeEventListener('click', handleCopy);
|
|
128
|
+
}
|
|
129
|
+
resolve();
|
|
130
|
+
return true;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
themodal.onNo = () => {
|
|
134
|
+
// Clean up the event listener
|
|
135
|
+
const modalContainer = document.querySelector('.micromodal-parent');
|
|
136
|
+
if (modalContainer) {
|
|
137
|
+
modalContainer.removeEventListener('click', handleCopy);
|
|
138
|
+
}
|
|
139
|
+
resolve();
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
themodal.open();
|
|
144
|
+
|
|
145
|
+
return promise;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Show API key with standard warning
|
|
150
|
+
*/
|
|
151
|
+
export function showApiKey(apiKey, username, expiresAt) {
|
|
152
|
+
return snippet('Sync API Key', apiKey, {
|
|
153
|
+
extraContent: `This key won't be shown again. Save it. Expires in 1 year.`
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Auto-initialize - cleanup any leftover modal elements
|
|
158
|
+
export function init() {
|
|
159
|
+
onDomReady(() => {
|
|
160
|
+
const micromodalParentElem = document.querySelector(".micromodal-parent");
|
|
161
|
+
if (micromodalParentElem) {
|
|
162
|
+
micromodalParentElem.remove();
|
|
163
|
+
document.body.style.overflow = "";
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Export to window for global access
|
|
169
|
+
export function exportToWindow() {
|
|
170
|
+
if (!window.hyperclay) {
|
|
171
|
+
window.hyperclay = {};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
window.hyperclay.ask = ask;
|
|
175
|
+
window.hyperclay.consent = consent;
|
|
176
|
+
window.hyperclay.tell = tell;
|
|
177
|
+
window.hyperclay.snippet = snippet;
|
|
178
|
+
window.hyperclay.showApiKey = showApiKey;
|
|
179
|
+
}
|