|
| 1 | +<template> |
| 2 | + <div |
| 3 | + class="relative mb-4 flex flex-col items-center text-white" |
| 4 | + ref="dropdownRef" |
| 5 | + > |
| 6 | + <!-- Split button: main button + dropdown trigger --> |
| 7 | + <div |
| 8 | + class="flex h-9 cursor-pointer rounded-full bg-[#202123] text-sm font-medium shadow-md" |
| 9 | + > |
| 10 | + <!-- Main button - launches ChatGPT directly --> |
| 11 | + <a |
| 12 | + class="flex items-center rounded-l-full px-3 duration-200 hover:bg-[#2d2f31]" |
| 13 | + :href="chatGPTLink" |
| 14 | + target="_blank" |
| 15 | + rel="noopener" |
| 16 | + > |
| 17 | + <img |
| 18 | + src="/assets/icons/chatgpt.svg" |
| 19 | + alt="ChatGPT" |
| 20 | + class="mr-2 h-6 w-6" |
| 21 | + /> |
| 22 | + Ask in ChatGPT |
| 23 | + </a> |
| 24 | + |
| 25 | + <!-- Dropdown trigger button --> |
| 26 | + <button |
| 27 | + class="flex w-6 items-center justify-center rounded-r-full border-l border-white/10 p-0 duration-200 hover:bg-[#2d2f31]!" |
| 28 | + @click.stop="toggleDropdown" |
| 29 | + aria-label="More options" |
| 30 | + > |
| 31 | + <Icon |
| 32 | + :class="{ 'rotate-180': isDropdownOpen }" |
| 33 | + class="transition-transform duration-200" |
| 34 | + icon="ep:arrow-down" |
| 35 | + height="12" |
| 36 | + width="12" |
| 37 | + /> |
| 38 | + </button> |
| 39 | + </div> |
| 40 | + |
| 41 | + <!-- Dropdown menu --> |
| 42 | + <div |
| 43 | + class="absolute top-full z-[1000] mt-2 min-w-[280px] rounded-xl border border-[#333] bg-[#1f1f1f] shadow-2xl" |
| 44 | + v-show="isDropdownOpen" |
| 45 | + > |
| 46 | + <a |
| 47 | + class="flex w-full items-center gap-3 rounded-xl px-4 py-3 text-left text-[#e0e0e0] transition-all duration-200 hover:bg-[#2a2a2a] hover:text-white focus:outline-none active:bg-[#333] active:text-white" |
| 48 | + :href="chatGPTLink" |
| 49 | + target="_blank" |
| 50 | + rel="noopener" |
| 51 | + @click="closeDropdown" |
| 52 | + > |
| 53 | + <div class="flex h-8 w-8 flex-shrink-0 items-center justify-center"> |
| 54 | + <img |
| 55 | + src="/assets/icons/chatgpt.svg" |
| 56 | + alt="ChatGPT" |
| 57 | + class="h-auto max-w-[80%]" |
| 58 | + /> |
| 59 | + </div> |
| 60 | + <div class="flex flex-1 flex-col gap-0.5"> |
| 61 | + <div class="text-sm font-medium">Open in ChatGPT</div> |
| 62 | + <div class="text-xs leading-tight text-[#999]"> |
| 63 | + Ask questions about this page |
| 64 | + </div> |
| 65 | + </div> |
| 66 | + <Icon |
| 67 | + class="flex-shrink-0 text-[#666] transition-colors duration-200 group-hover:text-[#999]" |
| 68 | + icon="ep:arrow-right" |
| 69 | + height="16" |
| 70 | + width="16" |
| 71 | + /> |
| 72 | + </a> |
| 73 | + |
| 74 | + <a |
| 75 | + class="flex w-full items-center gap-3 rounded-xl px-4 py-3 text-left text-[#e0e0e0] transition-all duration-200 hover:bg-[#2a2a2a] hover:text-white focus:outline-none active:bg-[#333] active:text-white" |
| 76 | + :href="claudeLink" |
| 77 | + target="_blank" |
| 78 | + rel="noopener" |
| 79 | + @click="closeDropdown" |
| 80 | + > |
| 81 | + <div class="flex h-8 w-8 flex-shrink-0 items-center justify-center"> |
| 82 | + <img |
| 83 | + src="/assets/icons/anthropic.svg" |
| 84 | + alt="Anthropic" |
| 85 | + class="h-auto max-w-[80%]" |
| 86 | + /> |
| 87 | + </div> |
| 88 | + <div class="flex flex-1 flex-col gap-0.5"> |
| 89 | + <div class="text-sm font-medium">Open in Claude</div> |
| 90 | + <div class="text-xs leading-tight text-[#999]"> |
| 91 | + Ask questions about this page |
| 92 | + </div> |
| 93 | + </div> |
| 94 | + <Icon |
| 95 | + class="flex-shrink-0 text-[#666] transition-colors duration-200 group-hover:text-[#999]" |
| 96 | + icon="ep:arrow-right" |
| 97 | + height="16" |
| 98 | + width="16" |
| 99 | + /> |
| 100 | + </a> |
| 101 | + |
| 102 | + <button |
| 103 | + class="flex w-full items-center gap-3 rounded-xl px-4! py-3! text-left text-[#e0e0e0] transition-all duration-200 hover:bg-[#2a2a2a]! focus:outline-none active:bg-[#333]! disabled:cursor-not-allowed disabled:opacity-50" |
| 104 | + @click="copyPageContent" |
| 105 | + :disabled="isCopying" |
| 106 | + > |
| 107 | + <div class="flex h-8 w-8 flex-shrink-0 items-center justify-center"> |
| 108 | + <Icon |
| 109 | + class="h-auto max-w-[80%]" |
| 110 | + icon="ep:copy-document" |
| 111 | + height="20" |
| 112 | + width="20" |
| 113 | + /> |
| 114 | + </div> |
| 115 | + <div class="flex flex-1 flex-col gap-0.5"> |
| 116 | + <div class="text-sm font-medium"> |
| 117 | + {{ isCopying ? 'Copied!' : 'Copy page' }} |
| 118 | + </div> |
| 119 | + <div class="text-xs leading-tight text-[#999]"> |
| 120 | + Copy page as Markdown for LLMs |
| 121 | + </div> |
| 122 | + </div> |
| 123 | + </button> |
| 124 | + </div> |
| 125 | + </div> |
| 126 | +</template> |
| 127 | + |
| 128 | +<script setup> |
| 129 | +import { ref, computed, onMounted, onUnmounted } from 'vue'; |
| 130 | +import { useRoute } from 'vitepress'; |
| 131 | +import TurndownService from 'turndown'; |
| 132 | +import { Icon } from '@iconify/vue'; |
| 133 | +
|
| 134 | +const route = useRoute(); |
| 135 | +const dropdownRef = ref(null); |
| 136 | +const isDropdownOpen = ref(false); |
| 137 | +const isCopying = ref(false); |
| 138 | +
|
| 139 | +// Initialize handleClickOutside ref |
| 140 | +const handleClickOutsideRef = ref(null); |
| 141 | +
|
| 142 | +const chatGPTLink = computed(() => { |
| 143 | + const fullUrl = `https://tools.docs.iex.ec${route.path}`; |
| 144 | + const prompt = `Please research and analyze this page: ${fullUrl} so I can ask you questions about it. Once you have read it, prompt me with any questions I have. Do not post content from the page in your response. Any of my follow up questions must reference the site I gave you.`; |
| 145 | + return `https://chatgpt.com/?hints=search&q=${encodeURIComponent(prompt)}`; |
| 146 | +}); |
| 147 | +
|
| 148 | +const claudeLink = computed(() => { |
| 149 | + const fullUrl = `https://tools.docs.iex.ec${route.path}`; |
| 150 | + const prompt = `Please research and analyze this page: ${fullUrl} so I can ask you questions about it. Once you have read it, prompt me with any questions I have. Do not post content from the page in your response. Any of my follow up questions must reference the site I gave you.`; |
| 151 | + return `https://claude.ai/new?q=${encodeURIComponent(prompt)}`; |
| 152 | +}); |
| 153 | +
|
| 154 | +const toggleDropdown = () => { |
| 155 | + isDropdownOpen.value = !isDropdownOpen.value; |
| 156 | +}; |
| 157 | +
|
| 158 | +const closeDropdown = () => { |
| 159 | + isDropdownOpen.value = false; |
| 160 | +}; |
| 161 | +
|
| 162 | +const copyPageContent = async () => { |
| 163 | + isCopying.value = true; |
| 164 | +
|
| 165 | + const turndownService = new TurndownService(); |
| 166 | + const mainContent = document.querySelector('.vp-doc'); |
| 167 | + const markdown = turndownService.turndown(mainContent); |
| 168 | +
|
| 169 | + await navigator.clipboard.writeText(markdown); |
| 170 | +
|
| 171 | + await new Promise((resolve) => setTimeout(resolve, 800)); |
| 172 | +
|
| 173 | + isCopying.value = false; |
| 174 | +}; |
| 175 | +
|
| 176 | +const handleClickOutside = (event) => { |
| 177 | + if (dropdownRef.value && !dropdownRef.value.contains(event.target)) { |
| 178 | + closeDropdown(); |
| 179 | + } |
| 180 | +}; |
| 181 | +
|
| 182 | +onMounted(() => { |
| 183 | + handleClickOutsideRef.value = handleClickOutside; |
| 184 | + document.addEventListener('click', handleClickOutsideRef.value); |
| 185 | +}); |
| 186 | +
|
| 187 | +onUnmounted(() => { |
| 188 | + document.removeEventListener('click', handleClickOutsideRef.value); |
| 189 | +}); |
| 190 | +</script> |
0 commit comments