CMDS Obsidian Settings

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

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

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

초기 버전(v1) 스크립트

스크립트 복사하기
"use strict"; // ==UserScript== // @name Obsidian Omnisearch in Google CMDS v0.1 // @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 // @description Injects Obsidian notes in Google search results with modern design // @author Simon Cambier, Modified by CMDSPACE // @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 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 */ #${resultsDivId} .obsidian-header { background: #7C3AED; color: white; padding: 16px 20px; border-radius: 12px 12px 0 0; display: flex; align-items: center; justify-content: space-between; border: 1px solid #7C3AED; border-bottom: none; } #${resultsDivId} .obsidian-header-title { display: flex; align-items: center; gap: 10px; font-size: 16px; font-weight: 600; color: white; } #${resultsDivId} .obsidian-header svg { width: 24px; height: 24px; filter: brightness(0) invert(1); } #${resultsDivId} .obsidian-settings-link { color: white; opacity: 0.9; font-size: 13px; text-decoration: none; transition: opacity 0.2s; } #${resultsDivId} .obsidian-settings-link:hover { opacity: 1; text-decoration: underline; } /* Content container */ #${resultsDivId} .obsidian-content { background: #ffffff; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 12px 12px; padding: 16px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); } /* Result items */ #${resultsDivId} [data-omnisearch-result] { background: #f8f9fa; border-radius: 8px; padding: 14px; margin-bottom: 12px; transition: all 0.2s ease; border: 1px solid transparent; cursor: pointer; } #${resultsDivId} [data-omnisearch-result]:last-child { margin-bottom: 0; } #${resultsDivId} [data-omnisearch-result]:hover { background: #ffffff; border-color: #dadce0; transform: translateX(-2px); box-shadow: 0 2px 8px rgba(0,0,0,0.08); } /* Result title */ #${resultsDivId} .LC20lb { color: #1a73e8; font-size: 15px; font-weight: 500; line-height: 1.4; margin-bottom: 6px; display: block; text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #${resultsDivId} a:hover .LC20lb { text-decoration: underline; } /* Meta information */ #${resultsDivId} .result-meta { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; } #${resultsDivId} .result-meta svg { width: 14px; height: 14px; } #${resultsDivId} .VuuXrf { color: #7C3AED; 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 */ #${resultsDivId} .VwiC3b { color: #4d5156; font-size: 13px; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } /* 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 */ @media (prefers-color-scheme: dark) { #${resultsDivId} .obsidian-header { background: #6B2EC5; color: white; border-color: #6B2EC5; } #${resultsDivId} .obsidian-settings-link { color: white; opacity: 0.9; } #${resultsDivId} .obsidian-settings-link:hover { opacity: 1; } #${resultsDivId} .obsidian-content { background: #202124; border-color: #3c4043; } #${resultsDivId} [data-omnisearch-result] { background: #303134; } #${resultsDivId} [data-omnisearch-result]:hover { background: #3c4043; border-color: #5f6368; } #${resultsDivId} .LC20lb { color: #8ab4f8; } #${resultsDivId} .VuuXrf { color: #9974F8; } #${resultsDivId} .dyjrff { color: #9aa0a6; } #${resultsDivId} .VwiC3b { color: #bdc1c6; } #${resultsDivId} #${loadingSpanId} { color: #9aa0a6; } } /* 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 const logo = `<svg height="1em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 256 256"> <style> .purple { fill: #7C3AED; } @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 for (const item of data) { const url = `obsidian://open?vault=${encodeURIComponent(item.vault)}&file=${encodeURIComponent(item.path)}`; const element = $(` <div data-omnisearch-result> <a href="${url}" style="text-decoration: none; color: inherit;"> <h3 class="LC20lb">${item.basename}</h3> <div class="result-meta"> ${logo} <span class="VuuXrf">Obsidian</span> </div> <div class="dyjrff">${item.path}</div> <div class="VwiC3b"> ${item.excerpt.replaceAll("<br />", " ").replaceAll("<br>", " ")} </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]) { 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"); 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"); // 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); } }); }); })();
이제는 여러 가지 레이아웃 상황에서도 깨지지 않고 잘 보입니다.

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

스크립트 복사하기