Greasy Fork镜像 还支持 简体中文。

Claude Project Delete Button

Add a delete button to Claude projects

  1. // ==UserScript==
  2. // @name Claude Project Delete Button
  3. // @namespace Violentmonkey Scripts
  4. // @match https://claude.ai/project/*
  5. // @grant none
  6. // @version 1.0
  7. // @author Elias Benbourenane
  8. // @description Add a delete button to Claude projects
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. function getProjectId() {
  16. const path = window.location.pathname;
  17. const match = path.match(/\/project\/([^\/]+)/);
  18. return match ? match[1] : null;
  19. }
  20.  
  21. function getOrgId() {
  22. return window.intercomSettings?.lastActiveOrgUUID;
  23. }
  24.  
  25. function createDeleteButton() {
  26. const deleteButton = document.createElement('button');
  27. deleteButton.className = `inline-flex
  28. items-center
  29. justify-center
  30. relative
  31. shrink-0
  32. can-focus
  33. select-none
  34. disabled:pointer-events-none
  35. disabled:opacity-50
  36. disabled:shadow-none
  37. disabled:drop-shadow-none
  38. text-text-300
  39. border-transparent
  40. transition
  41. font-styrene
  42. duration-300
  43. ease-[cubic-bezier(0.165,0.85,0.45,1)]
  44. hover:bg-bg-400
  45. aria-pressed:bg-bg-400
  46. aria-checked:bg-bg-400
  47. aria-expanded:bg-bg-300
  48. hover:text-text-100
  49. aria-pressed:text-text-100
  50. aria-checked:text-text-100
  51. aria-expanded:text-text-100
  52. h-8 w-8 rounded-md active:scale-95`.replace(/\s+/g, ' ');
  53.  
  54. deleteButton.type = 'button';
  55. deleteButton.title = 'Delete Project';
  56.  
  57. // Trash icon SVG
  58. deleteButton.innerHTML = `
  59. <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" viewBox="0 0 256 256" class="-translate-y-[0.5px]">
  60. <path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path>
  61. </svg>
  62. `;
  63.  
  64. deleteButton.addEventListener('click', handleDeleteClick);
  65.  
  66. return deleteButton;
  67. }
  68.  
  69. async function handleDeleteClick(event) {
  70. event.preventDefault();
  71.  
  72. const projectId = getProjectId();
  73. const orgId = getOrgId();
  74.  
  75. if (!projectId || !orgId) {
  76. alert('Unable to determine project or organization ID');
  77. return;
  78. }
  79.  
  80. if (!confirm('Are you sure you want to delete this project? This action cannot be undone.')) {
  81. return;
  82. }
  83.  
  84. try {
  85. const response = await fetch(`https://claude.ai/api/organizations/${orgId}/projects/${projectId}`, {
  86. method: 'DELETE',
  87. credentials: 'include',
  88. headers: {
  89. 'Content-Type': 'application/json',
  90. }
  91. });
  92.  
  93. if (response.ok) {
  94. alert('Project deleted successfully');
  95. window.location.href = 'https://claude.ai/';
  96. } else {
  97. throw new Error(`Delete failed: ${response.status} ${response.statusText}`);
  98. }
  99. } catch (error) {
  100. console.error('Error deleting project:', error);
  101. alert('Failed to delete project. Please try again.');
  102. }
  103. }
  104.  
  105. function addDeleteButton() {
  106. // Look for the container with the star and menu buttons
  107. const buttonContainer = document.querySelector('.flex.items-center.gap-1.ml-auto');
  108.  
  109. if (buttonContainer && !buttonContainer.querySelector('[title="Delete Project"]')) {
  110. const deleteButton = createDeleteButton();
  111.  
  112. // Insert the delete button before the menu button (last button)
  113. const menuButton = buttonContainer.lastElementChild;
  114. buttonContainer.insertBefore(deleteButton, menuButton);
  115. }
  116. }
  117.  
  118. let currentUrl = window.location.href;
  119.  
  120. function isProjectPage() {
  121. return window.location.pathname.startsWith('/project/');
  122. }
  123.  
  124. function handleUrlChange() {
  125. const newUrl = window.location.href;
  126. if (newUrl !== currentUrl) {
  127. currentUrl = newUrl;
  128.  
  129. if (isProjectPage()) {
  130. // Delay to allow page content to load
  131. setTimeout(addDeleteButton, 500);
  132. setTimeout(addDeleteButton, 1500);
  133. setTimeout(addDeleteButton, 3000);
  134. }
  135. }
  136. }
  137.  
  138. // Override history methods to detect programmatic navigation
  139. const originalPushState = history.pushState;
  140. const originalReplaceState = history.replaceState;
  141.  
  142. history.pushState = function(...args) {
  143. originalPushState.apply(history, args);
  144. setTimeout(handleUrlChange, 0);
  145. };
  146.  
  147. history.replaceState = function(...args) {
  148. originalReplaceState.apply(history, args);
  149. setTimeout(handleUrlChange, 0);
  150. };
  151.  
  152. function init() {
  153. if (isProjectPage()) {
  154. // Try to add the button immediately
  155. addDeleteButton();
  156.  
  157. // Also try after delays in case the page is still loading
  158. setTimeout(addDeleteButton, 500);
  159. setTimeout(addDeleteButton, 1500);
  160. setTimeout(addDeleteButton, 3000);
  161. }
  162.  
  163. // Set up a mutation observer to handle dynamic content loading
  164. const observer = new MutationObserver(function(mutations) {
  165. mutations.forEach(function(mutation) {
  166. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  167. // Only try to add button if we're on a project page
  168. if (isProjectPage()) {
  169. addDeleteButton();
  170. }
  171. }
  172. });
  173. });
  174.  
  175. observer.observe(document.body, {
  176. childList: true,
  177. subtree: true
  178. });
  179.  
  180. // Listen for browser back/forward navigation
  181. window.addEventListener('popstate', handleUrlChange);
  182.  
  183. // Periodic check to ensure button is present (fallback)
  184. setInterval(() => {
  185. if (isProjectPage()) {
  186. addDeleteButton();
  187. }
  188. }, 2000);
  189. }
  190.  
  191. if (document.readyState === 'loading') {
  192. document.addEventListener('DOMContentLoaded', init);
  193. } else {
  194. init();
  195. }
  196. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址