회의록 노트 템플릿 
Show more
Sign In

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) 스크립트

스크립트 복사하기
"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);
            }
        });
    });
})();