`; document.body.appendChild(el); return el; } const bubble = ensureBubble(); const elH = bubble.querySelector("#tt-title"); const elB = bubble.querySelector("#tt-body"); const elClose = bubble.querySelector(".tt-close"); // ---------------- Parse [[term|heading|body]] anywhere ---------------- const TOKEN_RE = /\[\[([^|\]]+)\|([^|\]]+)\|([^\]]+)\]\]/g; const BLOCK_SKIP = new Set(["SCRIPT","STYLE","NOSCRIPT","TEXTAREA","INPUT","SELECT","CODE","PRE","TEMPLATE","IFRAME"]); function shouldSkipTextNode(n){ let el = n.parentElement; while (el){ if (BLOCK_SKIP.has(el.tagName) || el.isContentEditable) return true; el = el.parentElement; } return false; } const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); const textNodes = []; while (walker.nextNode()){ const n = walker.currentNode; if (!n.nodeValue || shouldSkipTextNode(n)) continue; if (TOKEN_RE.test(n.nodeValue)) textNodes.push(n); TOKEN_RE.lastIndex = 0; } textNodes.forEach(node => { const frag = document.createDocumentFragment(); const insideLink = !!node.parentElement.closest("a"); let text = node.nodeValue, last = 0; TOKEN_RE.lastIndex = 0; let m; while ((m = TOKEN_RE.exec(text))){ if (m.index > last) frag.appendChild(document.createTextNode(text.slice(last, m.index))); const term=m[1].trim(), heading=m[2].trim(), body=m[3].trim(); const t = insideLink ? document.createElement("span") : document.createElement("button"); if (insideLink){ t.setAttribute("role","button"); t.setAttribute("tabindex","0"); } else { t.type="button"; } t.className="tt-trigger"; t.textContent=term; t.setAttribute("data-tt-h", heading); t.setAttribute("data-tt-b", body); t.setAttribute("aria-haspopup","dialog"); t.setAttribute("aria-expanded","false"); frag.appendChild(t); last = TOKEN_RE.lastIndex; } if (last = 2) return el; el = el.parentElement; } // Fallback: nearest non-inline container el = trigger.parentElement || document.body; while (el && el !== document.body){ const d = getComputedStyle(el).display; if (d !== "inline" && d !== "contents") return el; el = el.parentElement; } return document.body; } // Utility: child of `ancestor` that contains `target` (direct child) function directChildContaining(ancestor, target){ for (const ch of ancestor.children){ if (ch === target || ch.contains(target)) return ch; } return null; } function getElementTarget(e) { // If target is already an Element, use it if (e.target instanceof Element) return e.target; // Otherwise, walk the composed/path for the first Element const path = (typeof e.composedPath === 'function') ? e.composedPath() : []; for (const n of path) if (n instanceof Element) return n; return null; } // ---------------- Dim everything except the trigger branch (sibling branches only) ---------------- function dimAllOtherBranches(container, trigger){ undim(); // clear previous const dimEls = []; const wrappedTexts = []; const pathEls = []; // Build ELEMENT-only path [container -> ... -> trigger] const path = []; for (let el = trigger; el && el !== container; el = el.parentElement) path.push(el); path.push(container); path.reverse(); // At each ancestor level, find the *direct* child that leads to the trigger for (let i = 0; i { if (node.nodeType !== 3) return; // text only if (!node.nodeValue || !node.nodeValue.trim()) return; // If this text node sits inside branchChild, skip if (branchChild && branchChild.contains && branchChild.contains(node)) return; const span = document.createElement("span"); span.style.transition = `opacity ${DIM_EASE_MS}ms ease`; span.style.opacity = String(DIM_OPACITY); span.textContent = node.nodeValue; node.parentNode.replaceChild(span, node); wrappedTexts.push(span); }); // Keep a reference to the path elements (so we can explicitly restore opacity if needed) if (anc && anc.nodeType === 1) pathEls.push(anc); } // Hard-guard: explicitly set opacity:1 on the entire path to neutralize any inherited fade pathEls.forEach(el => { el.style.opacity = "1"; }); dimCtx = { container, dimEls, wrappedTexts, pathEls }; } function undim(){ if (!dimCtx) return; const { dimEls, wrappedTexts, pathEls } = dimCtx; // Animate back dimEls.forEach(el => { el.style.transition = `opacity ${DIM_EASE_MS}ms ease`; el.style.opacity = "1"; // remove inline style after the animation so we don't override site CSS setTimeout(() => { if (el) el.style.opacity = ""; }, DIM_EASE_MS + 50); }); wrappedTexts.forEach(span => { span.style.transition = `opacity ${DIM_EASE_MS}ms ease`; span.style.opacity = "1"; span.addEventListener("transitionend", () => { if (!span.parentNode) return; span.parentNode.replaceChild(document.createTextNode(span.textContent || ""), span); }, { once:true }); }); // Clear hard-guard on path pathEls.forEach(el => { if (el) el.style.opacity = ""; }); dimCtx = null; } // ---------------- Positioning (centered, edge-aware, flip) ---------------- function clamp(v,min,max){ return Math.max(min,Math.min(max,v)); } function measureBubbleForPlacement(){ const wasOpen = bubble.classList.contains("is-open"); if (!wasOpen){ bubble.style.visibility="hidden"; bubble.classList.add("is-open"); } const rect = bubble.getBoundingClientRect(); if (!wasOpen){ bubble.classList.remove("is-open"); bubble.style.visibility=""; } return { w: rect.width, h: rect.height }; } function placeAnchored(trigger){ const vw=innerWidth, vh=innerHeight; const r = trigger.getBoundingClientRect(); const { w, h } = measureBubbleForPlacement(); let left = r.left + (r.width/2) - (w/2); left = clamp(left, EDGE_PADDING, Math.max(EDGE_PADDING, vw - EDGE_PADDING - w)); const topBelow = r.bottom + OFFSET_Y; const spaceBelow = vh - topBelow - EDGE_PADDING; const placeBelow = spaceBelow >= h; let top = placeBelow ? topBelow : (r.top - h - OFFSET_Y); top = clamp(top, EDGE_PADDING, Math.max(EDGE_PADDING, vh - EDGE_PADDING - h)); bubble.style.left = left + "px"; bubble.style.top = top + "px"; const br = bubble.getBoundingClientRect(); if (br.bottom > vh - EDGE_PADDING){ bubble.style.maxHeight = (vh - 2*EDGE_PADDING) + "px"; bubble.style.overflowY = "auto"; } else { bubble.style.maxHeight = "none"; bubble.style.overflowY = "visible"; } } // ---------------- Open / Close (place → fade/scale) ---------------- function animateIn(){ bubble.style.transition = "none"; bubble.style.opacity = "0"; bubble.style.transform = "scale(0.95)"; void bubble.offsetWidth; bubble.style.transition = "opacity .18s ease, transform .18s ease"; bubble.style.opacity = "1"; bubble.style.transform = "scale(1)"; } function animateOut(done){ bubble.style.transition = "opacity .16s ease, transform .16s ease"; bubble.style.opacity = "0"; bubble.style.transform = "scale(0.95)"; const end = () => { bubble.removeEventListener("transitionend", end); done && done(); }; bubble.addEventListener("transitionend", end); setTimeout(end, 260); } function openFromTrigger(trigger){ if (current && current !== trigger) forceClose(); current = trigger; trigger.setAttribute("aria-expanded","true"); elH.textContent = trigger.getAttribute("data-tt-h") || ""; elB.textContent = trigger.getAttribute("data-tt-b") || ""; bubble.classList.add("is-open"); bubble.setAttribute("aria-hidden","false"); placeAnchored(trigger); animateIn(); const container = findTextContainer(trigger); dimAllOtherBranches(container, trigger); hoverCount = 0; cancelCloseTimer(); } function forceClose(){ if (!current) return; bubble.classList.remove("is-open"); bubble.setAttribute("aria-hidden","true"); current.setAttribute("aria-expanded","false"); current = null; undim(); hoverCount = 0; cancelCloseTimer(); } function closeWithAnim(){ if (!current) return; const t = current; animateOut(() => { bubble.classList.remove("is-open"); bubble.setAttribute("aria-hidden","true"); t.setAttribute("aria-expanded","false"); current = null; undim(); }); } function scheduleClose(){ cancelCloseTimer(); closeTimer = setTimeout(() => { if (hoverCount { if (isCoarse()) return; const target = getElementTarget(e); if (!target) return; const t = target.closest(".tt-trigger"); if (!t) return; onZoneEnter(); if (!current || current !== t) openFromTrigger(t); }; const handleLeave = (e) => { if (isCoarse()) return; const target = getElementTarget(e); if (!target) return; const t = target.closest(".tt-trigger"); if (!t) return; onZoneLeave(); }; document.addEventListener("pointerenter", handleEnter, true); document.addEventListener("mouseenter", handleEnter, true); document.addEventListener("pointerleave", handleLeave, true); document.addEventListener("mouseleave", handleLeave, true); // ---------------- Keyboard ---------------- document.addEventListener("focusin", (e) => { if (!e.target) return; const t = e.target.closest(".tt-trigger"); if (t) openFromTrigger(t); }); document.addEventListener("focusout", (e) => { if (!e.target) return; const t = e.target.closest(".tt-trigger"); if (t && current === t) closeWithAnim(); }); // ---------------- Mobile / coarse ---------------- document.addEventListener("pointerdown", (e) => { if (!isCoarse()) return; const t = e.target.closest(".tt-trigger"); if (!t) return; e.preventDefault(); e.stopPropagation(); if (current === t && bubble.classList.contains("is-open")) { closeWithAnim(); return; } openFromTrigger(t); }, true); document.addEventListener("click", (e) => { if (!isCoarse()) return; if (!bubble.classList.contains("is-open")) return; const inBubble = !!e.target.closest(".tt-bubble"); const onTrigger = !!e.target.closest(".tt-trigger"); if (!inBubble && !onTrigger) closeWithAnim(); }, true); // Close button + ESC elClose.addEventListener("click", closeWithAnim); document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeWithAnim(); }); // Reposition on resize/scroll while open const reposition = () => { if (!current) return; placeAnchored(current); }; addEventListener("resize", reposition, { passive: true }); addEventListener("scroll", reposition, { passive: true }); });

Claude를 즐겨찾는 앱에 연결하세요

Claude는 사용 중인 도구와 데이터베이스, 애플리케이션을 연동하여 더 관련성 있는 응답을 제공합니다. 모델 컨텍스트 프로토콜 기반의 다양한 커넥터 중에서 선택해 보세요.

커넥터 찾아보기

커넥터 찾아보기

감사합니다! 제출이 완료되었습니다!
앗! 양식을 제출하는 중 오류가 발생했습니다.
필터링
감사합니다! 제출이 완료되었습니다!
앗! 양식을 제출하는 중 오류가 발생했습니다.
직접 만든 커넥터를 제출하세요

새로운 커넥터는 모든 사용자가 Claude를 더 다양하게 활용할 수 있도록 지원합니다. 보유한 커넥터를 제출하면, 디렉터리 추가 여부를 검토하겠습니다.

시작하기
생산성
커뮤니케이션
Claude
Claude Code
January 30, 2026

Asana

Asana에 연결하여 작업, 프로젝트, 목표를 조율해 보세요

영업 및 마케팅
Claude
Claude Code
February 12, 2026

Attio

Search, manage, and update your Attio CRM from Claude

생명과학 및 헬스케어
Claude
Claude Code
March 13, 2026

Benchling

R&D 데이터, 소스 실험, 노트북에 연결해 보세요

생산성
항목을 찾을 수 없습니다.
March 18, 2026

Granola

회의를 위한 AI 노트패드

항목을 찾을 수 없습니다.
항목을 찾을 수 없습니다.
May 12, 2026

iManage

항목을 찾을 수 없습니다.
항목을 찾을 수 없습니다.
May 29, 2026

Ironclad

필터에 해당하는 커넥터가 없습니다

다른 검색어로 시도하거나 사용 중인 일부 필터를 해제해 보세요.

모든 필터 지우기
감사합니다! 제출이 완료되었습니다!
앗! 양식을 제출하는 중 오류가 발생했습니다.

자주 묻는 질문

Claude는 조직의 지식과 프로세스에 안전하게 통합되어 고품질의 업무 수행을 지원하는 신뢰 기반의 AI 파트너입니다. Claude는 조직 내의 다양한 비즈니스 기능 전반에서 생산성과 창의력을 크게 높여줍니다. Enterprise 플랜은 방대한 지식 업로드와 강화된 보안 및 사용자 관리 기능이 필요하며, 여러 팀이 심층적인 업무에 활용할 수 있도록 확장형 AI 솔루션을 찾는 조직을 위해 설계되었습니다.

Claude로 사용자 대상 기능이나 새로운 제품을 만들고자 하는 개발자라면, Anthropic API가 가장 적합한 선택입니다. API 플랜에 대한 자세한 내용은 영업팀에 문의하고, 시작하려면 개발자 문서를 살펴보세요.

디렉토리의 커넥터는 모델 컨텍스트 프로토콜(MCP)을 사용하여 타사 개발자가 구축하고 관리합니다. 각 커넥터 공급업체마다 자체 약관과 개인정보처리방침이 있으며, 일반적으로 연결 시 OAuth 인증 프로세스 중에 제공됩니다. 이 디렉토리에서 커넥터가 검토되고 게시되는 방식에 대한 자세한 내용은 MCP 디렉토리 이용 약관 MCP 디렉토리 정책을 참조하세요.

이전
다음