CMDS Obsidian Settings

옵시디언을 설정하고 체계를 구축하는 단계에서 참고하면 좋을 팁
설계 단계
5
CMDS Architecture
  • 커맨드스페이스
CMDS 목차 구조
  • 커맨드스페이스
CMDS Guide
  • 커맨드스페이스
CMDS 폴더 구조
  • 커맨드스페이스
활용 단계
4

Omnisearch와 구글 연동을 위한 개선된 Userscript

상태
활용 단계
작성자
  • 커맨드스페이스
작성시각
Omnisearch와 구글 검색창 연동하는 기능을 사용하시다보면 레이아웃이 깨지는 경우가 있습니다.
아래 스크립트는 해당 내용과 디자인 요소를 조금 개선한 커맨드스페이스 버전입니다.
Tampermonkey에 가셔서 새로운 스크립트로 추가하시고 사용하시면 됩니다.
혹 아직 설치가 안되어있으신 분들은 패스트캠퍼스 강의의 Part 2_Chapter 6-1을 확인하시거나 아래 링크를 참고해주세요.
준비가 되었다면 아래의 Userscript를 복사해서 사용하세요. 두 가지 버전이 있습니다.
v1
v2

초기 버전(v1) 스크립트

스크립트 복사하기
이제는 여러 가지 레이아웃 상황에서도 깨지지 않고 잘 보입니다.

임영록님 수정 버전(v2) 스크립트

스크립트 복사하기
"use strict"; // ==UserScript== // @name Obsidian Omnisearch in Google CMDS v0.1 - Custom // @namespace https://github.com/scambier/userscripts // @downloadURL https://github.com/scambier/userscripts/raw/master/dist/obsidian-omnisearch-google.user.js // @updateURL https://github.com/scambier/userscripts/raw/master/dist/obsidian-omnisearch-google.user.js // @version 0.4.2-custom // @description Injects Obsidian notes in Google search results with modern design - Custom version with dark header and no logos // @author Simon Cambier, Modified by CMDSPACE, Customized for user // @match https://google.com/* // @match https://www.google.com/* // @icon https://obsidian.md/favicon.ico // @require https://code.jquery.com/jquery-3.7.1.min.js // @require https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/master/gm_config.js // @require https://gist.githubusercontent.com/scambier/109932d45b7592d3decf24194008be4d/raw/9c97aa67ff9c5d56be34a55ad6c18a314e5eb548/waitForKeyElements.js // @grant GM.xmlHttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM.getValue // @grant GM.setValue // ==/UserScript== /* globals GM_config, jQuery, $, waitForKeyElements */ (function () { "use strict"; // Google's right "sidebar" that will contain the results div const sidebarSelector = "#rhs"; // The results div const resultsDivId = "OmnisearchObsidianResults"; // The "loading"/"no results" label const loadingSpanId = "OmnisearchObsidianLoading"; // Modern styles for the sidebar panel - CUSTOMIZED VERSION const injectStyles = () => { const style = document.createElement('style'); style.textContent = ` /* Main container styles */ #${resultsDivId} { margin-top: 20px; margin-bottom: 20px; padding: 0; width: 100%; min-width: 360px; box-sizing: border-box; } /* Header section - CUSTOMIZED: Light blue background without border */ #${resultsDivId} .obsidian-header { background: #F2F6FF; color: #1B0CAB; padding: 16px 20px; border-radius: 20px 20px 0 0; display: flex; align-items: center; justify-content: space-between; border: none; } #${resultsDivId} .obsidian-header-title { display: flex; align-items: center; gap: 10px; font-size: 16px; font-weight: normal; color: #1B0CAB; } /* Logo in header */ #${resultsDivId} .obsidian-header svg { width: 20px; height: 20px; } #${resultsDivId} .obsidian-settings-link { color: #1B0CAB; opacity: 0.7; font-size: 13px; text-decoration: none; transition: opacity 0.2s; } #${resultsDivId} .obsidian-settings-link:hover { opacity: 1; color: #1B0CAB; text-decoration: underline; } /* Content container */ #${resultsDivId} .obsidian-content { background: #F2F6FF; border: none; border-top: none; border-radius: 0 0 20px 20px; padding: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); } /* Result items */ #${resultsDivId} [data-omnisearch-result] { background: #ffffff; border-radius: 8px; padding: 14px; margin-bottom: 12px; transition: all 0.2s ease; border: none; cursor: pointer; } #${resultsDivId} [data-omnisearch-result]:last-child { margin-bottom: 0; } #${resultsDivId} [data-omnisearch-result]:hover { background: #ffffff; transform: translateX(-2px); box-shadow: 0 2px 8px rgba(0,0,0,0.08); } /* Result title - CUSTOMIZED: Smaller font size and reduced margin */ #${resultsDivId} .LC20lb { color: #1B0CAB; font-size: 12px; font-weight: 500; line-height: 1.4; margin-bottom: 3px; display: block; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #${resultsDivId} a:hover .LC20lb { text-decoration: underline; } /* Meta information - CUSTOMIZED: Hide logos */ #${resultsDivId} .result-meta { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; } /* CUSTOMIZED: Hide all logos in result items */ #${resultsDivId} .result-meta svg { display: none !important; } #${resultsDivId} .VuuXrf { color: #374151; font-size: 12px; font-weight: 500; } #${resultsDivId} .dyjrff { color: #80868b; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; display: block; } /* Excerpt text - CUSTOMIZED: Highlight search terms, 4 lines */ #${resultsDivId} .VwiC3b { color: #4d5156; font-size: 13px; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; margin-bottom: 8px; font-weight: 500; } /* Search term highlighting */ #${resultsDivId} .search-highlight { font-weight: bold; background: rgba(255, 255, 0, 0.2); padding: 1px 2px; border-radius: 2px; } /* Loading state */ #${resultsDivId} #${loadingSpanId} { display: block; text-align: center; color: #5f6368; padding: 20px; font-size: 14px; } /* Error state */ #${resultsDivId} .error-message { color: #d93025; padding: 16px; text-align: center; font-size: 14px; } #${resultsDivId} .error-message a { color: #1a73e8; text-decoration: none; } #${resultsDivId} .error-message a:hover { text-decoration: underline; } /* Dark mode support - CUSTOMIZED: Unified color scheme */ @media (prefers-color-scheme: dark) { /* Main container - no border */ #${resultsDivId} { border: none; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); } /* Header - Unified color */ #${resultsDivId} .obsidian-header { background: #2C303D; color: #e2e8f0; border: none; } #${resultsDivId} .obsidian-header-title { color: #e2e8f0; } #${resultsDivId} .obsidian-settings-link { color: #e2e8f0; opacity: 0.7; } #${resultsDivId} .obsidian-settings-link:hover { color: #e2e8f0; opacity: 1; } /* Content container - Unified color */ #${resultsDivId} .obsidian-content { background: #2C303D; border: none; } /* Individual result items - Unified color with subtle distinction */ #${resultsDivId} [data-omnisearch-result] { background: #2C303D; border-bottom: 1px solid rgba(255, 255, 255, 0.03); } #${resultsDivId} [data-omnisearch-result]:last-child { border-bottom: none; } #${resultsDivId} [data-omnisearch-result]:hover { background: #363A47; transform: translateX(-2px); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } /* Text colors */ #${resultsDivId} .LC20lb { color: #9974F8; } #${resultsDivId} .VuuXrf { color: #a0aec0; } #${resultsDivId} .dyjrff { color: #9aa0a6; } #${resultsDivId} .VwiC3b { color: #bdc1c6; } #${resultsDivId} #${loadingSpanId} { color: #9aa0a6; } /* Search highlighting in dark mode */ #${resultsDivId} .search-highlight { background: rgba(153, 116, 248, 0.3); color: #e2e8f0; } } /* Responsive adjustments */ @media (max-width: 1200px) { #${resultsDivId} .obsidian-header { padding: 14px 16px; } #${resultsDivId} .obsidian-header-title { font-size: 15px; } #${resultsDivId} .obsidian-content { padding: 12px; } } `; document.head.appendChild(style); }; // The `new GM_config()` syntax is not recognized by the TS compiler // @ts-ignore const gmc = new GM_config({ id: "ObsidianOmnisearchGoogle", title: "Omnisearch in Google - Configuration", fields: { port: { label: "HTTP Port", type: "text", default: "51361", }, nbResults: { label: "Number of results to display", type: "int", default: 3, }, }, events: { save: () => { location.reload(); }, init: () => { }, }, }); // Promise resolves when initialization completes const onInit = (config) => new Promise((resolve) => { let isInit = () => setTimeout(() => (config.isInit ? resolve() : isInit()), 0); isInit(); }); // Obsidian logo SVG const logo = `<svg height="1em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 256 256"> <style> .purple { fill: #1B0CAB; } @media (prefers-color-scheme: dark) { .purple { fill: #9974F8; } } </style> <path class="purple" d="M94.82 149.44c6.53-1.94 17.13-4.9 29.26-5.71a102.97 102.97 0 0 1-7.64-48.84c1.63-16.51 7.54-30.38 13.25-42.1l3.47-7.14 4.48-9.18c2.35-5 4.08-9.38 4.9-13.56.81-4.07.81-7.64-.2-11.11-1.03-3.47-3.07-7.14-7.15-11.21a17.02 17.02 0 0 0-15.8 3.77l-52.81 47.5a17.12 17.12 0 0 0-5.5 10.2l-4.5 30.18a149.26 149.26 0 0 1 38.24 57.2ZM54.45 106l-1.02 3.06-27.94 62.2a17.33 17.33 0 0 0 3.27 18.96l43.94 45.16a88.7 88.7 0 0 0 8.97-88.5A139.47 139.47 0 0 0 54.45 106Z"/><path class="purple" d="m82.9 240.79 2.34.2c8.26.2 22.33 1.02 33.64 3.06 9.28 1.73 27.73 6.83 42.82 11.21 11.52 3.47 23.45-5.8 25.08-17.73 1.23-8.67 3.57-18.46 7.75-27.53a94.81 94.81 0 0 0-25.9-40.99 56.48 56.48 0 0 0-29.56-13.35 96.55 96.55 0 0 0-40.99 4.79 98.89 98.89 0 0 1-15.29 80.34h.1Z"/><path class="purple" d="M201.87 197.76a574.87 574.87 0 0 0 19.78-31.6 8.67 8.67 0 0 0-.61-9.48 185.58 185.58 0 0 1-21.82-35.9c-5.91-14.16-6.73-36.08-6.83-46.69 0-4.07-1.22-8.05-3.77-11.21l-34.16-43.33c0 1.94-.4 3.87-.81 5.81a76.42 76.42 0 0 1-5.71 15.9l-4.7 9.8-3.36 6.72a111.95 111.95 0 0 0-12.03 38.23 93.9 93.9 0 0 0 8.67 47.92 67.9 67.9 0 0 1 39.56 16.52 99.4 99.4 0 0 1 25.8 37.31Z"/></svg>`; function omnisearch() { const port = gmc.get("port"); const nbResults = gmc.get("nbResults"); // Extract the ?q= part of the URL with URLSearchParams const params = new URLSearchParams(window.location.search); const query = params.get("q"); if (!query) return; injectLoadingLabel(); GM.xmlHttpRequest({ method: "GET", url: `http://localhost:${port}/search?q=${query}`, headers: { "Content-Type": "application/json", }, onload: function (res) { const data = JSON.parse(res.response); removeLoadingLabel(data.length > 0); // Keep the x first results data.splice(nbResults); const resultsContainer = $(`#${resultsDivId} .obsidian-content`); // Delete all existing data-omnisearch-result $("[data-omnisearch-result]").remove(); // Inject results - CUSTOMIZED: New order and search highlighting for (const item of data) { const url = `obsidian://open?vault=${encodeURIComponent(item.vault)}&file=${encodeURIComponent(item.path)}`; // Highlight search terms in excerpt let highlightedExcerpt = item.excerpt.replaceAll("<br />", " ").replaceAll("<br>", " "); if (query) { const queryTerms = query.split(' ').filter(term => term.length > 1); queryTerms.forEach(term => { const regex = new RegExp(`(${term})`, 'gi'); highlightedExcerpt = highlightedExcerpt.replace(regex, '<span class="search-highlight">$1</span>'); }); } const element = $(` <div data-omnisearch-result> <a href="${url}" style="text-decoration: none; color: inherit;"> <div class="VwiC3b"> ${highlightedExcerpt} </div> <h3 class="LC20lb">${item.basename}</h3> <div class="dyjrff">${item.path}</div> </a> </div> `); resultsContainer.append(element); } }, onerror: function (res) { console.log("Omnisearch error", res); const span = $("#" + loadingSpanId)[0]; if (span) { span.parentElement.innerHTML = ` <div class="error-message"> Error: Obsidian is not running or the Omnisearch server is not enabled. <br /><a href="obsidian://open">Open Obsidian</a> </div> `; } }, }); } function injectTitle() { const id = "OmnisearchObsidianConfig"; if (!$("#" + id)[0]) { // CUSTOMIZED: Header with logo const header = $(` <div class="obsidian-header"> <div class="obsidian-header-title"> ${logo} <span>Omnisearch Results</span> </div> <a id="${id}" class="obsidian-settings-link" href="#">Settings</a> </div> `); $(`#${resultsDivId}`).prepend(header); $(document).on("click", "#" + id, function (e) { e.preventDefault(); gmc.open(); }); } } function injectResultsContainer() { const resultsDiv = $(` <div id="${resultsDivId}"> <div class="obsidian-content"></div> </div> `); $(sidebarSelector).append(resultsDiv); // append instead of prepend to put at bottom } function injectLoadingLabel() { if (!$("#" + loadingSpanId)[0]) { const label = $(`<span id="${loadingSpanId}">Loading Obsidian results...</span>`); $(`#${resultsDivId} .obsidian-content`).append(label); } } function removeLoadingLabel(foundResults = true) { if (foundResults) { $("#" + loadingSpanId).remove(); } else { $("#" + loadingSpanId).text("No results found in Obsidian"); } } console.log("Loading Omnisearch injector CMDS v0.1 - Custom version"); let init = onInit(gmc); init.then(() => { // Inject styles injectStyles(); // Make sure the results container is there if (!$(sidebarSelector)[0]) { // Create sidebar with proper width if it doesn't exist $("#rcnt").append('<div id="rhs" style="min-width: 400px; flex-shrink: 0;"></div>'); } injectResultsContainer(); injectTitle(); omnisearch(); // Make an initial call console.log("Loaded Omnisearch injector CMDS v0.1 - Custom version"); // Keep the results at the bottom of sidebar waitForKeyElements(sidebarSelector, () => { // Move to bottom if other elements are added const omnisearchDiv = $(`#${resultsDivId}`); if (omnisearchDiv.next().length > 0) { omnisearchDiv.appendTo(sidebarSelector); } }); }); })();