Skip to main content
Home
Работи УМНО!
  • Login
  • Home
  • Контакти
  • Услуга: Видео към статия
  • About

Идея за проект: Управление на Chrome отметки чрез Git и файловата система

Sat, 07/18/2026 - 5:14pm by admin

Днешните браузъри третират отметките като част от самия браузър, а не като част от нашата работна среда. Това прави превключването между различни контексти – работа, лични проекти, обучение – ненужно зависимо от профили, синхронизации и външни услуги.

Когато линковете ни са организирани като стандартни файлове на диска, можем да впрегнем пълната сила на Git. Идеята е с една команда в терминала (git checkout) да подменяме файловата структура, което да кара Chrome да актуализира лентата си с отметки в същата секунда – без рестарт на браузъра и изцяло офлайн.

(Забележка: Този проект не съм го осъществил на практика. Той е тук, за да го ползвам като ръководство, когато го осъществявам.

Забележка 2: Статията съм я сглабял с AI и не съм имал време да вникна в нея – може да звучи дървеняшки :) )

Техническото предизвикателство и „Живият мост“

Основната софтуерна стена е, че Chrome работи с отметките единствено в оперативната си памет (RAM) и блокира външни промени по локалния си JSON файл. За да се заобиколи това ограничение, проектът предвижда архитектура от три синхронизирани елемента:

  • Локално Git хранилище: Директория на вашия Mac, където папките съответстват на категориите отметки, а текстовите файлове съдържат самите уеб адреси.

  • Фонов наблюдател (Скрипт): Услуга, която следи Git папката в реално време за промени чрез системните събития на macOS.

  • Персонално Chrome разширение: Компонент, който поддържа постоянна WebSocket връзка с фоновия скрипт. Когато скриптът засече промяна във файловете, той изпраща данните към разширението, което пренаписва отметките директно в паметта на Chrome за броени милисекунди.

Тази поредица ще проследи създаването на системата, която превръща браузъра в гъвкаво и напълно контролирано разширение на локалната ни работна среда.


Когато искаш да управляваш отметките си през файловата система и Git, ти всъщност се опитваш да свържеш две напълно различни софтуерни архитектури: статичния уеб браузър и динамичната операционна система.

Ето как изглежда концепцията на по-високо ниво, разделена на три основни стълба:

1. Проблемът с архитектурата на Chrome

За да разберем защо ни е нужен мост, трябва да разберем как мисли Chrome. Браузърът не е проектиран да работи с файлове в реално време. Когато го стартираш, той прочита целия си файл с отметки (Bookmarks JSON файла), „заключва“ тези данни в оперативната си памет (RAM) и работи само там.

Ако ти промениш файла на диска, Chrome просто не знае за това. Нещо повече – когато затвориш браузъра, той взема данните от паметта си и презаписва файла на диска, изтривайки всичко, което си променил външно.

2. Концептуалното решение: Моделът "Издател — Абонат" (Publisher-Subscriber)

Тъй като не можем да пипаме файла директно, трябва да излъжем Chrome през неговия официален интерфейс за програмиране (Chrome Extensions API). Концепцията се състои от три нива, които работят в затворен кръг:

[ Finder / Git ] ---> ( Наблюдател / Бекенд ) ---> [ WebSocket Мост ] ---> ( Chrome Разширение ) ---> [ Паметта на Chrome ]

Ниво 1: Хранилището (The Source of Truth)

Това е твоята Git папка. За нея отметките не са нищо повече от дървовидна структура:

  • Директориите представляват папките с отметки.

  • Файловете (например .url или .webloc) съдържат самия линк под формата на чист текст. Тъй като това са обикновени файлове, Git ги следи перфектно. Смяната на контекста става просто чрез превключване на Git клонове (git checkout work или git checkout personal).

Ниво 2: Наблюдателят (The File Watcher)

В background-а на твоя Mac работи малка бекенд услуга (написана на Node.js, Python или Go). Нейната единствена задача е да стои и да наблюдава Git папката. Тя се абонира за системните събития на macOS (чрез инструменти като FSEvents).

  • Когато смениш Git клона, macOS сигнализира на Наблюдателя: "Хей, в тази папка се промениха 50 файла".

  • Наблюдателят веднага прочита новата структура от папки и я превръща в един общ пакет данни (JSON).

Ниво 3: Живият мост (WebSockets & Extension)

Наблюдателят трябва да достави този JSON пакет до паметта на Chrome в същата милисекунда. Тъй като Chrome е изолиран, в него инсталираме наше микро-разширение.

  • Между Наблюдателя и Разширението се отваря WebSocket връзка – това е постоянна, двупосочна "телефонна линия", която не изисква презареждане на страници.

  • Когато Наблюдателят прати пакета по линията, Разширението го улавя вътре в браузъра.

  • Разширението използва вградената в Chrome функция chrome.bookmarks.update(). Тя заобикаля диска и казва директно на паметта на Chrome: "Изтрий старата лента и налей тези нови 20 линка веднага".

Резултатът: Лентата с отметки в браузъра ти се променя динамично пред очите ти, докато ти просто работиш във Finder или Терминала.

3. Как се връзва това с голямата ти цел (Всичко в един контекст)?

След като тази система е изградена на концептуално ниво, ти получаваш единна точка на управление.

Тъй като промяната на отметките вече се задейства от обикновена подмяна на файлове в папка, ти можеш да обвържеш всичко в един общ глобален macOS скрипт (например чрез AppleScript или приложение като Bunch):

Контекст "РАБОТА":

  1. Скриптът сменя десктоп тапета на работен.

  2. Скриптът сменя иконите в Dock.

  3. Скриптът отива в твоята папка и изпълнява git checkout work.

  4. Файловете се подменят -> Наблюдателят засича -> WebSocket предава -> Лентата в Chrome се актуализира с работните линкове автоматично.

По този начин превръщаш браузъра от затворена кутия в гъвкаво разширение на твоята операционна система. Напълно ли се припокрива тази концепция с това, което си представяше като работен процес?

Ето концептуалната схема, която илюстрира как информацията преминава от твоята файлова система директно в паметта на Chrome в реално време:

  ТВОЯТ MAC (Finder / Git)                  БЕКГРАУНД ПРОЦЕСИ                     БРАУЗЪР (Chrome)
┌──────────────────────────┐             ┌────────────────────┐               ┌───────────────────────┐
│                          │             │                    │               │                       │
│  📁 watched_bookmarks/   │             │   👀 НАБЛЮДАТЕЛ    │               │  🔌 CHROME EXTENSION  │
│  ├── 📁 Work/            │             │  (Node.js / Python)│               │  (Background Service) │
│  │   └── 📄 Jira.url     │ ──(1)──────>│                    │ ───(3)───────>│                       │
│  └── 📁 Personal/        │   Промяна   │  • Засича новите   │   WebSocket   │  • Слуша за събития   │
│      └── 📄 YouTube.url  │   във       │    файлове         │   Пакет       │  • Получава новия     │
│                          │   Finder    │  • Компилира ги    │   (JSON)      │    списък             │
│                          │   или Git   │    в JSON структура│               │                       │
└──────────────────────────┘             └────────────────────┘               └───────────────────────┘
             ▲                                                                            │
             │                                                                           (4)
            (2) git checkout personal                                                 Обновява
             │                                                                        директно
┌──────────────────────────┐                                                              ▼
│    🐙 GIT REPOSITORY     │                                                  ┌───────────────────────┐
│                          │                                                  │  🧠 OPERATIVE MEMORY  │
│  • Клон: work            │                                                  │        (RAM)          │
│  • Клон: personal        │                                                  │                       │
│                          │                                                  │  • Лентата с отметки  │
│  (Сменя целия контекст   │                                                  │    се пренаписва      │
│   с една команда)        │                                                  │    за милисекунди     │
└──────────────────────────┘                                                  └───────────────────────┘

Как да четеш схемата (Пътят на данните):

  1. Задействане (2 → 1): Ти сменяш контекста в терминала (напр. git checkout personal). Файловете в папката watched_bookmarks/ веднага се подменят физически от Git на твоя хард диск.

  2. Засичане (1 → 3): Наблюдателят (който използва системните събития на macOS) засича промяната незабавно. Той „прочита“ новите папки и линкове, превежда ги на езика на Chrome (JSON) и ги изпраща по „живата“ WebSocket телефонна линия.

  3. Реализация (3 → 4): Разширението на Chrome хваща този JSON по линията и използва директен достъп до паметта на браузъра. То изтрива старите отметки от екрана и налива новите – без да докосва заключените файлове на Chrome и без да изисква рестарт на браузъра.


Ръководство за осъществяване на динамични отметки

Това подробно ръководство ще ти покаже как да изградиш собствена двупосочна система за управление на отметки в реално време за Google Chrome на macOS.

Архитектурата се състои от три компонента:

  1. Локално хранилище (Git Repo): Папка на твоя Mac, съдържаща реални файлове и директории.

  2. Бекенд услуга (Node.js Сървър): Локален сървър, който следи папката за промени в реално време (чрез файловата система на macOS) и поддържа WebSocket връзка.

  3. Разширение за Chrome (Chrome Extension): Потребителски скрипт в браузъра, който слуша събитията от сървъра и използва chrome.bookmarks API, за да обновява интерфейса на Chrome за милисекунди, без презареждане.

Част 1: Архитектура на Системата

Преди да започнем с кода, нека дефинираме структурата на твоята работна папка. Създай папка с име ChromeBookmarkSync навсякъде на твоя Mac (например в потребителската ти директория ~/ChromeBookmarkSync).

Тя трябва да изглежда по следния начин:

~/ChromeBookmarkSync/
├── backend/
│   ├── server.js
│   ├── package.json
│   └── watched_bookmarks/  <-- Тук са реалните папки и линкове за Finder & Git
└── extension/
    ├── manifest.json
    └── background.js

Част 2: Изграждане на Локалния Бекенд (Node.js)

За бекенда ще използваме Node.js, тъй като има най-бързите и стабилни библиотеки за следене на промени по файловата система в реално време (chokidar) и поддръжка на WebSockets (ws).

1. Инициализация на проекта

Отвори терминала, влез в папка backend и инсталирай нужните пакети:

cd ~/ChromeBookmarkSync/backend
npm init -y
npm install chokidar ws

2. Създаване на server.js

Този скрипт ще върши черната работа: ще вдигне WebSocket сървър на порт 8080, ще сканира папка watched_bookmarks и при всяко създаване, триене или модифициране на файл/папка ще изпраща JSON команда към Chrome.

Отвори server.js и постави следния код:

const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const { WebSocketServer } = require('ws');

const WATCH_DIR = path.join(__dirname, 'watched_bookmarks');

// Уверяваме се, че папката за следене съществува
if (!fs.existsSync(WATCH_DIR)) {
    fs.mkdirSync(WATCH_DIR);
}

// Вдигане на WebSocket сървър
const wss = new WebSocketServer({ port: 8080 });
let chromeClient = null;

wss.on('connection', (ws) => {
    console.log('🌐 Chrome разширението се свърза успешно към локалния сървър.');
    chromeClient = ws;
    
    // При първоначално свързване изпращаме цялата структура на папките
    sendFullSync();

    ws.on('close', () => {
        console.log('❌ Връзката с Chrome бе прекъсната.');
        chromeClient = null;
    });
});

// Функция за изпращане на съобщения към браузъра
function sendToChrome(data) {
    if (chromeClient && chromeClient.readyState === 1) {
        chromeClient.send(JSON.stringify(data));
    }
}

// Функция, която прочита дървото от папки и файлове и го превръща в JSON
function buildTree(dirPath) {
    const stats = fs.statSync(dirPath);
    const name = path.basename(dirPath);

    if (stats.isDirectory()) {
        const children = fs.readdirSync(dirPath)
            .filter(child => !child.startsWith('.')) // Игнорираме скрити файлове (.DS_Store)
            .map(child => buildTree(path.join(dirPath, child)));
        
        return { type: 'folder', name, children };
    } else {
        // Четем URL от файла. Поддържаме както .url, така и чисти текстови файлове
        const content = fs.readFileSync(dirPath, 'utf-8');
        let url = content.trim();
        
        // Ако е стандартен Windows/Mac .url файл, извличаме линка
        if (name.endsWith('.url')) {
            const match = content.match(/URL=(.+)/);
            if (match) url = match[1].trim();
        }

        return { 
            type: 'url', 
            name: path.basename(name, path.extname(name)), 
            url 
        };
    }
}

function sendFullSync() {
    console.log('🔄 Извършване на пълна синхронизация...');
    try {
        const tree = buildTree(WATCH_DIR);
        sendToChrome({ action: 'FULL_SYNC', data: tree.children });
    } catch (err) {
        console.error('Грешка при сканиране на файловете:', err);
    }
}

// Инициализиране на Chokidar за следене на промени във Finder/Git в реално време
const watcher = chokidar.watch(WATCH_DIR, {
    ignored: /(^|[\/\\])\../, // Игнорираме скритото системно съдържание
    persistent: true,
    ignoreInitial: true // Пренебрегваме първоначалното зареждане, за него ползваме sendFullSync
});

// При каквато и да е промяна във Finder (Нов файл, изтриване, преименуване), пускаме ре-синк
watcher
    .on('add', () => sendFullSync())
    .on('change', () => sendFullSync())
    .on('unlink', () => sendFullSync())
    .on('addDir', () => sendFullSync())
    .on('unlinkDir', () => sendFullSync());

console.log(`🚀 Сървърът стартира. Следене на папка: ${WATCH_DIR}`);

Част 3: Изграждане на Разширението за Chrome (Chrome Extension)

Тъй като това разширение е лично твое и няма да се публикува в официалния уеб магазин, можем да използваме най-мощните му функции за вътрешно управление без рестрикции.

1. Създаване на manifest.json

Това е конфигурационният файл на разширението. Той казва на Chrome какви права искаме (bookmarks управление) и кои скриптове да се стартират в бекграунда.

Създай файла в ~/ChromeBookmarkSync/extension/manifest.json:

{
  "manifest_version": 3,
  "name": "Git & Finder Dynamic Bookmark Sync",
  "version": "1.0",
  "description": "Динамично синхронизиране на отметките от локални папки в реално време през WebSockets.",
  "permissions": [
    "bookmarks"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

2. Създаване на background.js

Това е скриптът (Service Worker), който работи непрекъснато зад кулисите на Chrome. Той ще се свърже към нашия Node.js сървър, ще изтрие старата структура в лентата с отметки и ще я пренапише на секундата според състоянието във Finder.

Създай файла в ~/ChromeBookmarkSync/extension/background.js:

let ws = null;

// Функция за свързване към локалния бекенд
function connectBackend() {
    ws = new WebSocket('ws://localhost:8080');

    ws.onopen = () => {
        console.log('Connected to local bookmark server.');
    };

    ws.onmessage = async (event) => {
        const message = JSON.parse(event.data);
        
        if (message.action === 'FULL_SYNC') {
            console.log('Received payload from Finder. Syncing Chrome GUI...');
            await applyBookmarksToChrome(message.data);
        }
    };

    // Автоматичен опит за повторно свързване, ако сървърът спре и стартира пак
    ws.onclose = () => {
        console.log('Socket closed. Retrying connection in 3 seconds...');
        setTimeout(connectBackend, 3000);
    };

    ws.onerror = (err) => {
        console.error('WebSocket Error:', err);
        ws.close();
    };
}

// Почистване на съществуващата лента (за да не дублираме данните при промяна)
async function clearBookmarkBar() {
    return new Promise((resolve) => {
        chrome.bookmarks.getTree((tree) => {
            // Корен 1 обикновено е "Bookmarks Bar" (Лентата с отметки)
            const bookmarkBar = tree[0].children[0];
            const children = bookmarkBar.children || [];
            
            // Изтриваме децата едно по едно
            const deletePromises = children.map(child => {
                return new Promise((res) => {
                    if (child.children) {
                        chrome.bookmarks.removeTree(child.id, res);
                    } else {
                        chrome.bookmarks.remove(child.id, res);
                    }
                });
            });
            
            Promise.all(deletePromises).then(resolve);
        });
    });
}

// Рекурсивна функция за изграждане на новото дърво в Chrome
async function createChromeNodes(nodes, parentId) {
    for (const node of nodes) {
        if (node.type === 'folder') {
            const createdFolder = await new Promise((res) => {
                chrome.bookmarks.create({
                    parentId: parentId,
                    title: node.name
                }, res);
            });
            if (node.children && node.children.length > 0) {
                await createChromeNodes(node.children, createdFolder.id);
            }
        } else if (node.type === 'url') {
            await new Promise((res) => {
                chrome.bookmarks.create({
                    parentId: parentId,
                    title: node.name,
                    url: node.url
                }, res);
            });
        }
    }
}

// Основен мениджър за пренаписване на GUI на Chrome
async function applyBookmarksToChrome(fsTree) {
    chrome.bookmarks.getTree(async (tree) => {
        const bookmarkBarId = tree[0].children[0].id;
        
        // 1. Изчистваме старите отметки
        await clearBookmarkBar();
        
        // 2. Наливане на новата структура от Finder
        await createChromeNodes(fsTree, bookmarkBarId);
        console.log('🎉 Chrome UI updated successfully in real-time!');
    });
}

// Стартиране на процеса при отваряне на браузъра
connectBackend();

Част 4: Инсталиране и Активиране на Системата

След като си написал файловете, следвай тези стъпки, за да пуснеш всичко в действие:

Стъпка 1: Стартирай локалния сървър

Отвори нов прозорец на Терминала и стартирай Node.js услугата:

cd ~/ChromeBookmarkSync/backend
node server.js

Ще видиш съобщение: 🚀 Сървърът стартира. Следене на папка... Дръж този терминал отворен.

Стъпка 2: Зареди разширението в Google Chrome

  1. Отвори Chrome и отиди на адрес: chrome://extensions/

  2. Горе вдясно включи бутона "Developer mode" (Режим за разработчици).

  3. Горе вляво ще се появи нов бутон: "Load unpacked" (Зареждане на непакетирано разширение).

  4. Цъкни го и избери папката extension (~/ChromeBookmarkSync/extension).

В същия миг разширението ще се зареди и в терминала на Node.js ще се изпише: 🌐 Chrome разширението се свърза успешно към локалния сървър.

Част 5: Тестване, Управление с Git и Работа във Finder

Сега е време за магията. Отвори Finder в папка ~/ChromeBookmarkSync/backend/watched_bookmarks.

  1. Създаване на папки: Създай нова папка във Finder, например [01] Разработка.

  2. Добавяне на линкове: Влез в нея. Създай обикновен текстов файл, преименувай го на Github.url и вътре запиши текста: URL=[https://github.com](https://github.com)

  3. Резултат в реално време: Погледни лентата с отметки на Chrome (Bookmarks Bar). Дори без да докосваш браузъра, папката и линкът ще се появят там за броени милисекунди!

Свързване с Git

Влез в папка watched_bookmarks през терминала и я направи Git хранилище:

cd ~/ChromeBookmarkSync/backend/watched_bookmarks
git init
git add .
git commit -m "Initial context bookmarks setup"

Когато искаш да смениш контекста на отметките си напълно (например си приключил работа и превключваш на лична вълна):

git checkout personal-context

В момента, в който Git превключи клона и подмени файловете в папката, Node.js засича промяната и Chrome обновява цялата ти лента с отметки пред очите ти.

Част 6: Потенциални оптимизации за напреднали

Тази система е стабилна основа. Тъй като вече имаш работеща IPC (Inter-process communication) архитектура, ето какво можеш да добавиш в бъдеще:

  1. Двупосочна синхронизация: Ако добавиш отметка директно през Chrome (с натискане на звездата), в background.js можеш да уловиш събитието chrome.bookmarks.onCreated.addListener. След това разширението може да изпрати обратно по WebSocket съобщение към Node.js сървъра, който от своя страна да създаде реалния файл на диска чрез fs.writeFileSync.

  2. Интеграция с macOS Shortcuts: Тъй като това се контролира от папка и файлове, можеш да направиш глобален macOS Shortcut, който изпълнява git checkout [контекст] в тази папка. Обвържи го с клавишна комбинация и ще сменяш отметките в Chrome, десктоп тапета и Finder с едно натискане на клавиатурата.

 

 

 

  • Log in to post comments