Better AutomationAnywhere

Enhanced Automation Anywhere developer experience. Working at CR Version 36.0.0

当前为 2025-04-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Better AutomationAnywhere
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.4.9
  5. // @description Enhanced Automation Anywhere developer experience. Working at CR Version 36.0.0
  6. // @author jamir-boop
  7. // @match *://*.automationanywhere.digital/*
  8. // @icon https://cmpc-1dev.my.automationanywhere.digital/favicon.ico
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_registerMenuCommand
  12. // @license MIT
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. "use strict";
  17. let activePredictionIndex = -1; // Track the active (highlighted) prediction
  18. let currentPredictionActions = []; // Store current predictions' actions for keyboard navigation
  19.  
  20. // Universal Copy and Paste functionality with 3 slots
  21.  
  22. // Register menu commands for selecting copy/paste slots
  23. GM_registerMenuCommand("Copy to Slot 1", () => copyToSlot(1));
  24. GM_registerMenuCommand("Copy to Slot 2", () => copyToSlot(2));
  25. GM_registerMenuCommand("Copy to Slot 3", () => copyToSlot(3));
  26. GM_registerMenuCommand("Paste from Slot 1", () => pasteFromSlot(1));
  27. GM_registerMenuCommand("Paste from Slot 2", () => pasteFromSlot(2));
  28. GM_registerMenuCommand("Paste from Slot 3", () => pasteFromSlot(3));
  29.  
  30. // Commands and their aliases mapping to functions
  31. const commandsWithAliases = {
  32. addVariable: {
  33. action: addVariable,
  34. aliases: ["adv", "addvar", "add variable"],
  35. description: "Shows dialog to create a new variable",
  36. },
  37. showVariables: {
  38. action: showVariables,
  39. aliases: ["v", "showvars", "list variables", "variables"],
  40. description: "Shows variables in sidebar",
  41. },
  42. deleteUnusedVariables: {
  43. action: deleteUnusedVariables,
  44. aliases: ["duv", "delete unused", "remove unused variables"],
  45. description: "Shows dialog to select and delete unused variables",
  46. },
  47. redirectToPrivateRepository: {
  48. action: redirectToPrivateRepository,
  49. aliases: ["p", "private", "private bots"],
  50. description: "Redirects to the private bots folder",
  51. },
  52. redirectToActivityHistorical: {
  53. action: redirectToActivityHistorical,
  54. aliases: ["historical", "history","activity historical"],
  55. description: "Redirects to the activities historical tab",
  56. },
  57. redirectToAuditLog: {
  58. action: redirectToAuditLog,
  59. aliases: ["audit", "audit log"],
  60. description: "Redirects to the activities historical tab",
  61. },
  62. showHelp: {
  63. action: function () {
  64. showHelp();
  65. },
  66. aliases: ["help", "man", "show help"],
  67. description: "Displays help information for available commands",
  68. },
  69. };
  70.  
  71. //============ Command palette START ============
  72. // Helper function to get DOM elements dynamically
  73. function getCommandPalette() {
  74. return document.getElementById("commandPalette");
  75. }
  76.  
  77. function getCommandInput() {
  78. return document.getElementById("commandInput");
  79. }
  80.  
  81. function getCommandPredictions() {
  82. return document.getElementById("commandPredictions");
  83. }
  84.  
  85. // Toggle palette visibility
  86. function togglePaletteVisibility() {
  87. const commandPalette = getCommandPalette();
  88. if (commandPalette.classList.contains("command_palette--visible")) {
  89. commandPalette.classList.remove("command_palette--visible");
  90. commandPalette.classList.add("command_palette--hidden");
  91. getCommandInput().value = ""; // Clear input on hide
  92. clearPredictions(); // Clear predictions on hide
  93. } else {
  94. commandPalette.classList.remove("command_palette--hidden");
  95. commandPalette.classList.add("command_palette--visible");
  96. getCommandInput().focus(); // Focus on the input field when showing the palette
  97. }
  98. }
  99.  
  100. function clearPredictions() {
  101. getCommandPredictions().innerHTML = "";
  102. }
  103.  
  104. function updatePredictions(input) {
  105. clearPredictions();
  106.  
  107. if (!input) return;
  108.  
  109. // Check for ":<number>" syntax to scroll to a line
  110. const jumpToLineMatch = input.match(/^:(\d+)$/);
  111. if (jumpToLineMatch) {
  112. const lineNumber = parseInt(jumpToLineMatch[1], 10);
  113. const predictionItem = document.createElement("div");
  114. predictionItem.classList.add("command_prediction-item");
  115. predictionItem.innerHTML = `<strong>Go to line ${lineNumber}</strong>`;
  116. predictionItem.addEventListener("click", () => {
  117. scrollToLineNumber(lineNumber);
  118. clearPredictions();
  119. togglePaletteVisibility();
  120. });
  121. getCommandPredictions().appendChild(predictionItem);
  122. return; // Skip the normal alias match
  123. }
  124.  
  125. Object.entries(commandsWithAliases).forEach(
  126. ([commandKey, { action, aliases, description }]) => {
  127. const match = aliases.find((alias) =>
  128. alias.startsWith(input.toLowerCase())
  129. );
  130. if (match) {
  131. const predictionItem = document.createElement("div");
  132. predictionItem.classList.add("command_prediction-item");
  133. predictionItem.innerHTML = `<strong>${match}</strong> - ${description}`;
  134. predictionItem.addEventListener("click", () => {
  135. getCommandInput().value = match;
  136. executeCommand(action);
  137. clearPredictions();
  138. });
  139. getCommandPredictions().appendChild(predictionItem);
  140. }
  141. }
  142. );
  143. }
  144.  
  145. // Function to setup event listeners for commandInput
  146. function setupCommandInputEventListeners() {
  147. const commandInput = getCommandInput(); // Ensure we're getting the current element
  148.  
  149. if (commandInput) {
  150. // Input event listener for updating predictions
  151. commandInput.addEventListener("input", function () {
  152. updatePredictions(this.value);
  153. });
  154.  
  155. // Keydown event listener for navigating and selecting predictions
  156. commandInput.addEventListener("keydown", navigatePredictions);
  157. }
  158. }
  159.  
  160. // Execute command function based on input or selected prediction
  161. function executeCommand(action) {
  162. if (action) {
  163. action();
  164. } else {
  165. showHelp(); // Show help or error if command is unknown
  166. }
  167. togglePaletteVisibility(); // Hide palette after executing command
  168. }
  169.  
  170. function navigatePredictions(e) {
  171. let commandPredictions = getCommandPredictions();
  172. const items = commandPredictions.getElementsByClassName(
  173. "command_prediction-item",
  174. );
  175. if (!items.length) {
  176. if (e.key === "Escape") {
  177. togglePaletteVisibility();
  178. e.preventDefault();
  179. }
  180. return;
  181. }
  182.  
  183. // Automatically select the prediction if there's only one and Enter is pressed
  184. if (items.length === 1 && e.key === "Enter") {
  185. items[0].click(); // Execute the single available command
  186. e.preventDefault();
  187. return;
  188. }
  189.  
  190. if (["ArrowDown", "ArrowUp", "Enter"].includes(e.key)) {
  191. e.preventDefault(); // Prevent default only for navigation keys
  192.  
  193. if (e.key === "ArrowDown") {
  194. activePredictionIndex = (activePredictionIndex + 1) % items.length;
  195. updateActivePrediction(items);
  196. } else if (e.key === "ArrowUp") {
  197. if (activePredictionIndex <= 0) {
  198. activePredictionIndex = items.length - 1;
  199. } else activePredictionIndex -= 1;
  200. updateActivePrediction(items);
  201. } else if (e.key === "Enter" && activePredictionIndex >= 0) {
  202. items[activePredictionIndex].click();
  203. }
  204. } else if (e.key === "Escape") {
  205. togglePaletteVisibility();
  206. e.preventDefault();
  207. }
  208. }
  209.  
  210. function updateActivePrediction(items) {
  211. Array.from(items).forEach((item, index) => {
  212. item.classList.toggle("active", index === activePredictionIndex);
  213. });
  214. }
  215.  
  216. // Toggle command palette visibility with Shift+C
  217. document.addEventListener("keydown", function (e) {
  218. if (e.altKey && e.key === "p") {
  219. e.preventDefault();
  220. //insertCommandPalette();
  221. insertCustomEditorPaletteButtons();
  222. togglePaletteVisibility();
  223. }
  224. });
  225.  
  226. // Shortcuts to show Actions/Variables
  227. document.addEventListener("keydown", function (e) {
  228. if (e.code === "KeyA" && e.altKey) {
  229. addAction();
  230. e.preventDefault(); // Prevent default action of Alt+A
  231. }
  232. });
  233.  
  234. document.addEventListener("keydown", function (e) {
  235. if (e.code === "KeyV" && e.altKey) {
  236. showVariables();
  237. e.preventDefault(); // Prevent default action of Alt+V
  238. }
  239. });
  240.  
  241. // Shortcuts to toggle sidebar
  242. document.addEventListener("keydown", function (e) {
  243. if (e.ctrlKey && e.code === "KeyD") {
  244. (function () {
  245. toogleToolbar();
  246. })();
  247. e.preventDefault();
  248. }
  249. });
  250.  
  251. // Function to toggle toolbar
  252. function toogleToolbar() {
  253. document
  254. .querySelector(
  255. "div.editor-layout__resize:nth-child(2) > button:nth-child(2)",
  256. )
  257. .click();
  258. }
  259.  
  260. // Function to check if toolbar is opened
  261. function checkPaletteState() {
  262. let paletteElement = document.querySelector(".editor-layout__palette");
  263. let width = paletteElement.offsetWidth; // Get the actual width
  264.  
  265. if (width <= 8) {
  266. return "closed";
  267. } else {
  268. return "opened";
  269. }
  270. }
  271.  
  272. // Features
  273. function addAction() {
  274. const state = checkPaletteState();
  275.  
  276. if (state === "closed") {
  277. toogleToolbar(); // Open the toolbar if it's closed
  278. }
  279.  
  280. try {
  281. document
  282. .querySelector(
  283. "div.editor-palette__accordion:nth-child(2) > div:nth-child(1) > header:nth-child(1) > div:nth-child(1) > button:nth-child(1) > div:nth-child(1) > div:nth-child(2)",
  284. )
  285. .click();
  286. } catch {}
  287. try {
  288. document
  289. .querySelector(
  290. '.editor-palette-search__cancel button[type="button"][tabindex="-1"]',
  291. )
  292. .click();
  293. } catch {}
  294. }
  295.  
  296. async function addVariable() {
  297. // Ensure toolbar is open
  298. if (checkPaletteState() === "closed") {
  299. toogleToolbar();
  300. await new Promise(resolve => setTimeout(resolve, 800)); // wait for toolbar animation
  301. }
  302.  
  303. // Click the "Add Variable" button
  304. const addButton = document.querySelector('div.editor-palette__accordion header button');
  305. if (addButton) {
  306. addButton.click();
  307. } else {
  308. console.warn("Add Variable button not found");
  309. return;
  310. }
  311.  
  312. // Wait a bit for dialog to appear
  313. await new Promise(resolve => setTimeout(resolve, 500));
  314.  
  315. // Click the "Create" button if it's there
  316. const createButton = document.querySelector('button[name="create"]');
  317. if (createButton) {
  318. createButton.click();
  319. } else {
  320. console.warn("Create button not found");
  321. return;
  322. }
  323.  
  324. // Wait for the confirm bar to appear and click confirm
  325. await new Promise(resolve => setTimeout(resolve, 500));
  326. const confirmButton = document.querySelector('div.action-bar--theme_default button:nth-child(2)');
  327. if (confirmButton) {
  328. confirmButton.click();
  329. } else {
  330. console.warn("Confirm button not found");
  331. }
  332. }
  333.  
  334.  
  335. async function showVariables() {
  336. const state = checkPaletteState();
  337.  
  338. if (state === "closed") {
  339. toogleToolbar();
  340. await new Promise(r => setTimeout(r, 1000)); // wait for the toolbar to open
  341. }
  342.  
  343. for (let i = 0; i < 10; i++) {
  344. const el = document.querySelector(
  345. 'span.clipped-text.clipped-text--no_wrap.editor-palette-section__header-title[title="Variables"]'
  346. );
  347. if (el) {
  348. el.click();
  349. return;
  350. }
  351. await new Promise(r => setTimeout(r, 300)); // retry every 300ms
  352. }
  353. }
  354.  
  355. function showTriggers() {
  356. const state = checkPaletteState();
  357.  
  358. if (state === "closed") {
  359. toogleToolbar(); // Open the toolbar if it's closed
  360. }
  361. document
  362. .querySelector(
  363. 'span.clipped-text.clipped-text--no_wrap.editor-palette-section__header-title[title="Triggers"]',
  364. )
  365. ?.click();
  366. }
  367.  
  368. async function deleteUnusedVariables() {
  369. showVariables();
  370.  
  371. await new Promise(resolve => setTimeout(resolve, 1000));
  372. let dropdownMenu = document.querySelector("button.action-bar__item--is_menu:nth-child(5)");
  373. dropdownMenu.click();
  374.  
  375. await new Promise(resolve => setTimeout(resolve, 1000));
  376. let duvButton = document.querySelector(".dropdown-options.g-scroller button.rio-focus--inset_4px:nth-child(2)");
  377. duvButton.click();
  378. }
  379.  
  380. function openLinkInNewTab(url) {
  381. var newWindow = window.open(url, "_blank");
  382. if (newWindow) {
  383. newWindow.blur();
  384. window.focus();
  385. } else {
  386. alert("Pop-up blocked. Please allow pop-ups for this website.");
  387. }
  388. }
  389.  
  390. function scrollToLineNumber(lineNumber) {
  391. // Inject highlight styles if not already present
  392. if (!document.getElementById('highlight-line-style')) {
  393. const style = document.createElement('style');
  394. style.id = 'highlight-line-style';
  395. style.textContent = `
  396. .highlight-line {
  397. background-color: yellow;
  398. transition: background-color 0.5s ease;
  399. }
  400. .highlight-line::after {
  401. background-color: yellow !important;
  402. transition: background-color 0.5s ease;
  403. }
  404. `;
  405. document.head.appendChild(style);
  406. }
  407.  
  408. const lineElements = document.querySelectorAll('.taskbot-canvas-list-node > .taskbot-canvas-list-node__number');
  409.  
  410. if (lineNumber < 1 || lineNumber > lineElements.length) {
  411. console.warn(`Line ${lineNumber} is out of range. Total lines: ${lineElements.length}`);
  412. return;
  413. }
  414.  
  415. const targetElement = lineElements[lineNumber - 1];
  416.  
  417. // Scroll smoothly to the element
  418. targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
  419.  
  420. // Add highlight class
  421. targetElement.classList.add('highlight-line');
  422.  
  423. // Remove the highlight after a short delay
  424. setTimeout(() => {
  425. targetElement.classList.remove('highlight-line');
  426. }, 2000);
  427. }
  428.  
  429. function showHelp() {
  430. const modalOverlay = document.createElement('div');
  431. const modal = document.createElement('div');
  432. const modalContent = document.createElement('div');
  433. const closeButton = document.createElement('button');
  434. const signature = document.createElement('div');
  435.  
  436. modalOverlay.style.position = 'fixed';
  437. modalOverlay.style.top = '0';
  438. modalOverlay.style.left = '0';
  439. modalOverlay.style.width = '100vw';
  440. modalOverlay.style.height = '100vh';
  441. modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
  442. modalOverlay.style.display = 'flex';
  443. modalOverlay.style.justifyContent = 'center';
  444. modalOverlay.style.alignItems = 'center';
  445. modalOverlay.style.zIndex = '1000';
  446. modalOverlay.style.fontSize = '16px';
  447.  
  448. modal.style.backgroundColor = 'white';
  449. modal.style.padding = '20px';
  450. modal.style.borderRadius = '8px';
  451. modal.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
  452. modal.style.maxWidth = '800px';
  453. modal.style.width = '80%';
  454. modal.style.position = 'relative';
  455.  
  456. let helpContent = "<h3>List of Commands:</h3><ul>";
  457.  
  458. for (let command in commandsWithAliases) {
  459. const { aliases, description } = commandsWithAliases[command];
  460. helpContent += `<li><b>${aliases.join(', ')}</b>: ${description}</li>`;
  461. }
  462. helpContent += `<li><b>:<i>line</i></b>: Scrolls to a specific line number (e.g. <code>:25</code>)</li>`;
  463. helpContent += "</ul>";
  464.  
  465. helpContent += `
  466. <h4>Keyboard Shortcuts:</h4>
  467. <ul>
  468. <li><b>Alt + P</b>: Open the command palette</li>
  469. <li><b>Alt + V</b>: Show variables</li>
  470. <li><b>Alt + A</b>: Show actions</li>
  471. </ul>
  472.  
  473. <h4>Clipboard Slots:</h4>
  474. <ul>
  475. <li>Use the context menu (Tampermonkey menu) to:
  476. <ul>
  477. <li><code>Copy to Slot 1</code>, <code>Slot 2</code>, <code>Slot 3</code></li>
  478. <li><code>Paste from Slot 1</code>, <code>Slot 2</code>, <code>Slot 3</code></li>
  479. </ul>
  480. </li>
  481. <li>You can also use the rocket icons in the top action bar to quickly copy/paste</li>
  482. </ul>
  483. `;
  484.  
  485. modalContent.innerHTML = helpContent;
  486.  
  487. closeButton.textContent = 'Close';
  488. closeButton.style.marginTop = '10px';
  489. closeButton.style.padding = '8px 16px';
  490. closeButton.style.border = 'none';
  491. closeButton.style.backgroundColor = 'var(--color_background_interactive)';
  492. closeButton.style.color = 'white';
  493. closeButton.style.cursor = 'pointer';
  494. closeButton.style.borderRadius = '4px';
  495.  
  496. signature.innerHTML = `<a href="https://github.com/Jamir-boop/automationanywhere-improvements.git" target="_blank" style="text-decoration: none; color: #888; font-size: 12px;">made by jamir-boop</a>`;
  497. signature.style.position = 'absolute';
  498. signature.style.bottom = '8px';
  499. signature.style.right = '12px';
  500.  
  501. modal.appendChild(modalContent);
  502. modal.appendChild(closeButton);
  503. modal.appendChild(signature);
  504. modalOverlay.appendChild(modal);
  505. document.body.appendChild(modalOverlay);
  506.  
  507. function closeModal() {
  508. document.body.removeChild(modalOverlay);
  509. }
  510.  
  511. modalOverlay.addEventListener('click', (e) => {
  512. if (e.target === modalOverlay) {
  513. closeModal();
  514. }
  515. });
  516. document.addEventListener('keydown', (e) => {
  517. if (e.key === 'Escape') {
  518. closeModal();
  519. }
  520. });
  521. closeButton.addEventListener('click', closeModal);
  522. }
  523.  
  524. //============ Command palette stuff END ============
  525.  
  526. //============ Feat snippets START ============
  527. function generateEmojiString() {
  528. const emojis = [
  529. "😀",
  530. "😃",
  531. "😄",
  532. "😁",
  533. "😆",
  534. "😅",
  535. "😂",
  536. "🤣",
  537. "😊",
  538. "😇",
  539. "🙂",
  540. "🙃",
  541. "😉",
  542. "😌",
  543. "😍",
  544. "🥰",
  545. "😘",
  546. "😗",
  547. "😙",
  548. "😚",
  549. "😋",
  550. "😛",
  551. "😝",
  552. "😜",
  553. "🤪",
  554. "🤨",
  555. "🧐",
  556. "🤓",
  557. "😎",
  558. "🤩",
  559. "🥳",
  560. "😏",
  561. "😒",
  562. "😞",
  563. "😔",
  564. "😟",
  565. "😕",
  566. "🙁",
  567. "😣",
  568. "😖",
  569. "😫",
  570. "😩",
  571. "🥺",
  572. "😢",
  573. "😭",
  574. "😤",
  575. "😠",
  576. "😡",
  577. "🤬",
  578. "🤯",
  579. "😳",
  580. "🥵",
  581. "🥶",
  582. "😱",
  583. "😨",
  584. "😰",
  585. "😥",
  586. "😓",
  587. "🤗",
  588. "🤔",
  589. "🤭",
  590. "🤫",
  591. "🤥",
  592. "😶",
  593. "😐",
  594. "😑",
  595. "😬",
  596. "🙄",
  597. "😯",
  598. "😦",
  599. "😧",
  600. "😮",
  601. "😲",
  602. "🥱",
  603. "😴",
  604. "🤤",
  605. "😪",
  606. "😵",
  607. "🤐",
  608. "🥴",
  609. "🤢",
  610. "🤮",
  611. "🤧",
  612. "😷",
  613. "🤒",
  614. "🤕",
  615. "🤑",
  616. "🤠",
  617. "😈",
  618. "👿",
  619. "👹",
  620. "👺",
  621. "🤡",
  622. "💩",
  623. "👻",
  624. "💀",
  625. "☠️",
  626. "👽",
  627. "👾",
  628. "🤖",
  629. "🎃",
  630. "😺",
  631. "😸",
  632. "😹",
  633. "😻",
  634. "😼",
  635. "😽",
  636. "🙀",
  637. "😿",
  638. "😾",
  639. ];
  640. let uniqueString = "";
  641.  
  642. for (let i = 0; i < 10; i++) {
  643. uniqueString += emojis[Math.floor(Math.random() * emojis.length)];
  644. }
  645.  
  646. return uniqueString;
  647. }
  648.  
  649. //============ Feat snippets END ============
  650. //============ Feat custom selector Variables/Actions/Triggers START============
  651.  
  652. // Define updateActiveButton in the outer scope
  653. function updateActiveButton() {
  654. const activeSection = document.querySelector(
  655. ".editor-palette-section__header--is_active .clipped-text__string--for_presentation",
  656. )?.innerText;
  657. const buttons = document.querySelectorAll(".customActionVariableButton");
  658.  
  659. buttons.forEach((button) => {
  660. if (button.textContent === activeSection) {
  661. button.classList.add("buttonToolbarActive");
  662. } else {
  663. button.classList.remove("buttonToolbarActive");
  664. }
  665. });
  666. }
  667.  
  668. function insertCustomEditorPaletteButtons() {
  669. if (document.getElementById("customActionVariableButtons")) {
  670. console.log("Custom buttons already added.");
  671. return;
  672. }
  673.  
  674. const containerDiv = document.createElement("div");
  675. containerDiv.id = "customActionVariableButtons";
  676.  
  677. const variableButton = document.createElement("button");
  678. variableButton.className = "customActionVariableButton";
  679. variableButton.textContent = "Variables";
  680. variableButton.onclick = function () {
  681. showVariables();
  682. updateActiveButton();
  683. };
  684.  
  685. const actionButton = document.createElement("button");
  686. actionButton.className = "customActionVariableButton";
  687. actionButton.textContent = "Actions";
  688. actionButton.onclick = function () {
  689. addAction();
  690. updateActiveButton();
  691. };
  692.  
  693. const triggerButton = document.createElement("button");
  694. triggerButton.className = "customActionVariableButton";
  695. triggerButton.textContent = "Triggers";
  696. triggerButton.onclick = function () {
  697. showTriggers();
  698. updateActiveButton();
  699. };
  700.  
  701. containerDiv.appendChild(variableButton);
  702. containerDiv.appendChild(actionButton);
  703. containerDiv.appendChild(triggerButton);
  704.  
  705. const palette = document.querySelector(".editor-layout__palette");
  706. if (palette) {
  707. palette.appendChild(containerDiv);
  708. } else {
  709. console.log(".editor-layout__palette not found.");
  710. return;
  711. }
  712.  
  713. const style = document.createElement("style");
  714. style.textContent = `
  715. #customActionVariableButtons {
  716. display: flex;
  717. width: 100%;
  718. height: 38px !important;
  719. background: white;
  720. }
  721. #customActionVariableButtons button {
  722. all: unset;
  723. font-size: .85rem;
  724. font-weight: 300;
  725. cursor: pointer;
  726. margin: 4px;
  727. border-radius: 5px;
  728. border: 1px solid transparent;
  729. background-color: transparent;
  730. color: #3c5e83;
  731. flex-grow: 1;
  732. text-align: center;
  733. transition: background-color 0.3s;
  734. }
  735. #customActionVariableButtons button:hover {
  736. background-color: #dae9f3;
  737. }
  738. .buttonToolbarActive {
  739. border: 1px solid #3c5e83 !important;
  740. text-shadow: 0.5px 0 0 #3c5e83 , -0.01px 0 0 #3c5e83 !important;
  741. }
  742. .editor-palette.g-box-sizing_border-box {
  743. margin-top: 38px;
  744. }
  745. `;
  746. document.head.appendChild(style);
  747. }
  748. //============ Feat custom selector Variables/Actions/Triggers END============
  749. //============ Feat UNIVERSAL COPY/PASTE START============
  750. // Function to copy data to the specified slot
  751. function copyToSlot(slot) {
  752. // Trigger the copy action in the UI
  753. const copyButton = document.querySelector(".aa-icon-action-clipboard-copy--shared");
  754. if (copyButton) {
  755. copyButton.click();
  756.  
  757. // Retrieve the JSON string from localStorage
  758. const globalClipboardJSON = localStorage.getItem('globalClipboard');
  759.  
  760. // Parse the JSON and store it in the specific slot using Tampermonkey storage
  761. try {
  762. const clipboardData = JSON.parse(globalClipboardJSON);
  763. clipboardData.uid = "🔥🔥🔥"; // Reset UID
  764. GM_setValue(`universalClipboardSlot${slot}`, JSON.stringify(clipboardData));
  765. } catch (error) {
  766. console.error("Failed to copy data to slot:", error);
  767. }
  768. }
  769. }
  770.  
  771. // Function to paste data from the specified slot
  772. function pasteFromSlot(slot) {
  773. // Retrieve the JSON string from the specified slot in Tampermonkey storage
  774. const clipboardData = GM_getValue(`universalClipboardSlot${slot}`);
  775.  
  776. if (!clipboardData) {
  777. alert(`No data in Slot ${slot}`);
  778. return;
  779. }
  780.  
  781. // Generate a new unique ID for this session
  782. let emojiUid = generateEmojiString();
  783.  
  784. // Replace the UID and store it back into localStorage
  785. let modifiedData = clipboardData.replace(/🔥🔥🔥/g, emojiUid);
  786. localStorage.setItem('globalClipboard', modifiedData);
  787. localStorage.setItem('globalClipboardUid', `"${emojiUid}"`);
  788.  
  789. // Ensure the paste button is available
  790. const pasteButton = document.querySelector(".aa-icon-action-clipboard-paste--shared");
  791. if (pasteButton) {
  792. // Trigger the paste action
  793. setTimeout(() => {
  794. pasteButton.click();
  795. }, 500); // Adjust the timeout as needed
  796. }
  797. }
  798.  
  799. function insertUniversalCopyPasteButtons(attempt = 1) {
  800. setTimeout(() => {
  801. const actionBar = document.querySelector('.action-bar--theme_info');
  802.  
  803. // If actionBar is found and the buttons have not been added yet
  804. if (actionBar && !actionBar.querySelector('.universalCopy')) {
  805. const separator = document.createElement('div');
  806. separator.className = 'action-bar__separator';
  807. actionBar.appendChild(separator);
  808.  
  809. // Create the Universal Copy button
  810. const copyButton = document.createElement('button');
  811. copyButton.className = 'universalCopy rio-focus rio-focus--inset_0 rio-focus--border-radius_4px rio-focus--has_element-focus-visible rio-bare-button g-reset-element rio-bare-button--is_interactive rio-bare-button--rio_interactive-softest rio-bare-button--is_parent rio-bare-button--is_clickable rio-bare-button--size_14px rio-bare-button--is_square rio-bare-button--square_26x26 action-bar__item action-bar__item--is_action taskbot-editor__toolbar__action';
  812. copyButton.setAttribute('data-button-loading', 'false');
  813. copyButton.setAttribute('data-button-working', 'false');
  814. copyButton.setAttribute('data-button-ready', 'true');
  815. copyButton.setAttribute('name', 'shared-clipboard-copy');
  816. copyButton.setAttribute('type', 'button');
  817. copyButton.setAttribute('tabindex', '-1');
  818. copyButton.setAttribute('aria-label', 'Copy to shared clipboard');
  819. copyButton.setAttribute('data-poppy-parentid', '1');
  820. copyButton.innerHTML = `<span class="icon fa fa-rocket icon--block"></span>`;
  821. copyButton.title = 'Universal Copy';
  822. copyButton.onclick = universalCopy;
  823. actionBar.appendChild(copyButton);
  824.  
  825. // Create the Universal Paste button
  826. const pasteButton = document.createElement('button');
  827. pasteButton.className = 'universalPaste rio-focus rio-focus--inset_0 rio-focus--border-radius_4px rio-focus--has_element-focus-visible rio-bare-button g-reset-element rio-bare-button--is_interactive rio-bare-button--rio_interactive-softest rio-bare-button--is_parent rio-bare-button--is_clickable rio-bare-button--size_14px rio-bare-button--is_square rio-bare-button--square_26x26 action-bar__item action-bar__item--is_action taskbot-editor__toolbar__action';
  828. pasteButton.setAttribute('data-button-loading', 'false');
  829. pasteButton.setAttribute('data-button-working', 'false');
  830. pasteButton.setAttribute('data-button-ready', 'true');
  831. pasteButton.setAttribute('name', 'shared-clipboard-paste');
  832. pasteButton.setAttribute('type', 'button');
  833. pasteButton.setAttribute('tabindex', '-1');
  834. pasteButton.setAttribute('aria-label', 'Paste from shared clipboard');
  835. pasteButton.setAttribute('data-poppy-parentid', '1');
  836. pasteButton.innerHTML = `<span class="icon fa fa-rocket icon--block" style="transform: rotate(180deg);"></span>`;
  837. pasteButton.title = 'Universal Paste';
  838. pasteButton.onclick = universalPaste;
  839. actionBar.appendChild(pasteButton);
  840. } else if (attempt < 3) {
  841. // If not found, retry up to 3 times
  842. insertUniversalCopyPasteButtons(attempt + 1);
  843. }
  844. }, 1000 * attempt); // Delay increases with each attempt
  845. }
  846.  
  847.  
  848. function universalCopy() {
  849. // Trigger the copy action in the UI
  850. document.querySelector(".aa-icon-action-clipboard-copy--shared").click();
  851.  
  852. // Retrieve the JSON string from local storage
  853. const globalClipboardJSON = localStorage.getItem('globalClipboard');
  854.  
  855. // Parse the JSON string into an object
  856. let globalClipboard = {};
  857. try {
  858. globalClipboard = JSON.parse(globalClipboardJSON);
  859. } catch(e) {
  860. console.error("Error parsing JSON:", e);
  861. return; // Exit if there is a parsing error
  862. }
  863.  
  864. // Update the "uid" key to 🔥🔥🔥
  865. globalClipboard.uid = "🔥🔥🔥";
  866.  
  867. // Stringify the modified object and store it in Tampermonkey storage
  868. GM_setValue('universalClipboard', JSON.stringify(globalClipboard));
  869. }
  870.  
  871. function universalPaste() {
  872. // Click on copy to activate paste button
  873. document.querySelector(".aa-icon-action-clipboard-copy--shared").click();
  874.  
  875. // Retrieve the JSON string from Tampermonkey storage
  876. let universalClipboard = GM_getValue('universalClipboard')
  877.  
  878. // Write the JSON string to local storage after replacing single quotes and UID
  879. if (universalClipboard) {
  880. let emojiUid = generateEmojiString();
  881. universalClipboard = universalClipboard.replace(/'/g, '"');
  882. universalClipboard = universalClipboard.replace(/🔥🔥🔥/g, emojiUid);
  883.  
  884. localStorage.setItem("globalClipboard", universalClipboard);
  885. localStorage.setItem("globalClipboardUid", `"${emojiUid}"`);
  886. }
  887.  
  888. // Wait for a second before triggering the paste action
  889. setTimeout(() => {
  890. document.querySelector(".aa-icon-action-clipboard-paste--shared").click();
  891. }, 1000);
  892. }
  893. //============ Feat UNIVERSAL COPY/PASTE END============
  894. //============ Feat redirect utility START ============
  895. function redirectToPath(targetPath) {
  896. const currentUrl = window.location.href;
  897.  
  898. // Match the base URL, keeping everything up to the first part of the domain and protocol.
  899. const pattern = /^(https:\/\/[^\/]*\.automationanywhere\.digital)/;
  900.  
  901. // Extract the base URL using the pattern
  902. const match = currentUrl.match(pattern);
  903.  
  904. if (match) {
  905. const baseUrl = match[1]; // Get the base URL (e.g., https://aa-saleseng-us-4sbx.cloud.automationanywhere.digital)
  906. const newUrl = baseUrl + targetPath; // Append the target path
  907. window.location.href = newUrl; // Redirect to the new URL
  908. } else {
  909. console.error("Pattern didn't match. The URL might not be in the expected format.");
  910. }
  911. }
  912.  
  913. // Function to redirect to the private bots repository
  914. function redirectToPrivateRepository() {
  915. const privateBotsPath = '/#/bots/repository/private/';
  916. redirectToPath(privateBotsPath);
  917. }
  918.  
  919. // Function to redirect to the activity historical page
  920. function redirectToActivityHistorical() {
  921. const activityHistoricalPath = '/#/activity/historical/';
  922. redirectToPath(activityHistoricalPath);
  923. }
  924.  
  925. // Function to redirect to the audit log page
  926. function redirectToAuditLog() {
  927. const auditLog = '/#/audit';
  928. redirectToPath(auditLog);
  929. }
  930.  
  931. // This function ensures the left navigation panel is collapsed and removes any inline width
  932. // - If already collapsed (.pathfinder--is_collapsed exists), it just removes the inline width
  933. // - If not collapsed, it clicks the "Collapse" button and removes the width after it's collapsed
  934.  
  935. function removeInlineWidth() {
  936. const nav = document.querySelector('.main-layout__navigation');
  937. const pathfinderCollapsed = document.querySelector('.pathfinder--is_collapsed');
  938.  
  939. // If already collapsed, just remove inline width
  940. if (pathfinderCollapsed) {
  941. if (nav?.style?.width) {
  942. nav.style.removeProperty('width');
  943. }
  944. return;
  945. }
  946.  
  947. // If not collapsed, try to collapse it
  948. const collapseButton = document.querySelector('button[aria-label="Collapse"]');
  949. if (collapseButton) {
  950. collapseButton.click();
  951.  
  952. setTimeout(() => {
  953. if (nav?.style?.width) {
  954. nav.style.removeProperty('width');
  955. }
  956. }, 500);
  957. } else {
  958. console.warn('Collapse button not found');
  959. }
  960. }
  961.  
  962. //============ Feat redirect utility END ============
  963. //============ Feat insert command palette START ============
  964. // Insterts the command palette
  965. function insertCommandPalette(retryCount = 0) {
  966. // Check if the palette was already inserted
  967. if (document.querySelector("#commandPalette")) {
  968. console.log("Command palette already inserted.");
  969. return;
  970. }
  971.  
  972. // Create the container div and set its inner HTML
  973. const containerDiv = document.createElement("div");
  974. containerDiv.id = "commandPalette";
  975. containerDiv.className = "command_palette--hidden";
  976. containerDiv.innerHTML = `
  977. <input type="text" id="commandInput" placeholder="Enter command...">
  978. <div id="commandPredictions" class="command_predictions"></div>
  979. `;
  980.  
  981. // Append the container div to the body
  982. document.body.appendChild(containerDiv);
  983.  
  984. // Create and insert CSS
  985. const css = `
  986. #commandPalette * {
  987. font-size: 1.15rem;
  988. font-family: Museo Sans,sans-serif;
  989. }
  990.  
  991. #commandPalette {
  992. position: fixed;
  993. top: 50%;
  994. left: 50%;
  995. transform: translate(-50%, -50%);
  996. background-color: white;
  997. border-radius: 10px 10px 0 0;
  998. display: flex;
  999. flex-direction: column;
  1000. align-items: center;
  1001. min-width: 30vw;
  1002. max-width: 80vw;
  1003. width: 600px;
  1004. z-index: 99999;
  1005.  
  1006. box-shadow: 0px 0px 0px 5000px #00000054;
  1007. }
  1008.  
  1009. #commandInput,
  1010. #commandInput:focus-visible,
  1011. #commandInput:active {
  1012. unset: all;
  1013. padding: 10px;
  1014. width: 93%;
  1015. margin-bottom: 10px;
  1016. border: 2px solid transparent;
  1017. border-radius: 5px;
  1018. }
  1019.  
  1020. #commandPalette:focus,
  1021. #commandPalette:active {
  1022. border-color: orange;
  1023. }
  1024.  
  1025. #commandPredictions {
  1026. position: absolute;
  1027. top: 100%;
  1028. left: 0;
  1029. width: 100%;
  1030. background-color: white;
  1031. box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  1032. border-radius: 0 0 10px 10px;
  1033. max-height: 200px;
  1034. overflow-y: auto;
  1035. z-index: 100000;
  1036. }
  1037.  
  1038. .command_prediction-item.active {
  1039. background-color: #f0f0f0;
  1040. }
  1041.  
  1042. .command_prediction-item strong {
  1043. font-weight: bold;
  1044. }
  1045.  
  1046. .command_prediction-item {
  1047. padding: 8px;
  1048. cursor: pointer;
  1049. border-bottom: 1px solid #eee;
  1050. }
  1051.  
  1052. .command_prediction-item:hover,
  1053. .command_prediction-item.active {
  1054. background-color: #f0f0f0;
  1055. }
  1056.  
  1057. @keyframes fadeIn {
  1058. from { opacity: 0; transform: translate(-50%, -50%) scale(0.85); }
  1059. to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  1060. }
  1061.  
  1062. @keyframes fadeOut {
  1063. from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  1064. to { opacity: 0; transform: translate(-50%, -50%) scale(0.95); }
  1065. }
  1066.  
  1067. .command_palette--visible {
  1068. display: block;
  1069. animation: fadeIn 0.25s ease-out forwards;
  1070. }
  1071.  
  1072. .command_palette--hidden {
  1073. animation: fadeOut 0.25s ease-out forwards;
  1074. display: none;
  1075. pointer-events: none;
  1076. opacity: 0;
  1077. z-index: -1;
  1078. }
  1079. `;
  1080.  
  1081. const style = document.createElement("style");
  1082. style.type = "text/css";
  1083. style.appendChild(document.createTextNode(css));
  1084. document.head.appendChild(style);
  1085.  
  1086. setupCommandInputEventListeners();
  1087.  
  1088. // Check if the palette was successfully inserted, and if not, retry
  1089. if (!document.querySelector("#commandPalette")) {
  1090. if (retryCount < 5) {
  1091. console.log(`Insert failed, retrying... (${retryCount + 1}/5)`);
  1092. setTimeout(() => insertCommandPalette(retryCount + 1), 3000);
  1093. } else {
  1094. console.error("Failed to insert command palette after 5 retries.");
  1095. }
  1096. } else {
  1097. console.log("Command palette successfully inserted.");
  1098. }
  1099. }
  1100. //============ Feat insert command palette END ============
  1101.  
  1102. //============ Call insert functions START ============
  1103. function executeStartFunctionsRepeatedly() {
  1104. let count = 0;
  1105. const intervalId = setInterval(() => {
  1106. insertCommandPalette();
  1107. insertCustomEditorPaletteButtons();
  1108. setInterval(function () {updateActiveButton();}, 1000);
  1109. insertUniversalCopyPasteButtons();
  1110. removeInlineWidth()
  1111.  
  1112. count++;
  1113. if (count >= 3) {
  1114. clearInterval(intervalId);
  1115. }
  1116. }, 5000); // Execute every 5 seconds
  1117. }
  1118.  
  1119. if (document.readyState === "loading") {
  1120. // The document is still loading, wait for DOMContentLoaded
  1121. document.addEventListener(
  1122. "DOMContentLoaded",
  1123. executeStartFunctionsRepeatedly,
  1124. );
  1125. } else {
  1126. // The `DOMContentLoaded` event has already fired, execute immediately
  1127. executeStartFunctionsRepeatedly();
  1128. }
  1129.  
  1130. let lastHref = document.location.href;
  1131. setInterval(function () {
  1132. const currentHref = document.location.href;
  1133.  
  1134. if (lastHref !== currentHref) {
  1135. lastHref = currentHref;
  1136. insertCommandPalette();
  1137. insertCustomEditorPaletteButtons();
  1138. insertUniversalCopyPasteButtons();
  1139. removeInlineWidth();
  1140. }
  1141. }, 5000);
  1142.  
  1143. //============ Call insert functions END ============
  1144. })();