Около года назад (30 мая 2023, если быть более точным) в Хроме появилась возможность отображать интерфейсные элементы расширений не только во всплывающем окне (popup.html), но и в боковой панели которая отображается сбоку (справа) от текущей отображаемой страницы. Внешне боковая панель расширения похожа на боковые панели, встроенные в сам браузер, такие как «Show all bookmarks» или «Grouped history». На мой взгляд, у этой фичи большой потенциал: В таком формате можно пробовать реализовать более сложные сценарии взаимодействия с пользователем, включающие как более сложный интерфейс расширения, так и стандартные возможности взаимодействия с текущей открытой страницей. При этом мне показалось что новость об этом релизе прошла ниже радаров и в публикациях тема использования этой боковой панели оказалась недостаточно раскрыта, так что я решил написать эту обзорную статью (в первую очередь, в надежде на содержательные комментарии по теме со стороны тех, у кого есть опыт в разработке расширений).

Вначале кратко скажу о технических аспектах. Затем будет небольшой обзор некоторых возможных вариантов использования. И в конце расскажу про небольшое расширение, которое я «разработал» сам.

Для желающих получить начальные представления о разработке расширений для Хрома думаю может быть полезно предварительно ознакомиться с этой статьей (хотя я наверно предложил бы для таких упражнений вместо ChatGPT попробовать использовать Курсор с Композером и новым Клодом).

Техническая сторона

С точки зрения реализации главные отличия «панельного» расширения от «обычного» заключаются в следующем:

  • Проект должен вместо файла popup.html включать в себя sidepanel.html. Это обычная веб-страница, которая, собственно, и отображается в боковой панели вместо попапа.

  • В манифесте также нужно прописать sidepanel вместо попапа: "side_panel": { "default_path": "sidepanel.html" } и "permissions": [ "sidePanel" ]

Боковая панель расширения открывается также как и попап — по нажатию на кнопку этого расширения. В принципе можно реализовать ограничение что бы панель открывалась только если в текущей вкладке открыт определенный сайт (site-specific extension). Реализация показана в примере из документации. Но при копипасте этого когда в мое расширение это ограничение почему-то не заработало — панель продолжала открываться на всех сайтах. В итоге я воспользовался тем что мне написал Claude в Курсоре.

service_worker
const CHAT_ORIGIN = 'https://chatgpt.com';

chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && tab.url && tab.url.startsWith(CHAT_ORIGIN)) {
    chrome.tabs.sendMessage(tabId, { action: "checkContentScriptInjected" }, response => {
      if (chrome.runtime.lastError) {
        // Content script is not injected, so inject it
        chrome.scripting.executeScript({
          target: { tabId: tabId },
          files: ['content.js']
        });
      }
    });

    // Set side panel options
    if (chrome.sidePanel) {
      chrome.sidePanel.setOptions({
        tabId,
        path: 'sidepanel.html',
        enabled: true
      }).catch(error => console.error('Error setting side panel options:', error));
    }
  } else {
    // Disable the side panel for other pages
    if (chrome.sidePanel) {
      chrome.sidePanel.setOptions({
        tabId,
        enabled: false
      }).catch(error => console.error('Error disabling side panel:', error));
    }
  }
});

// Add this to ensure the side panel is opened when the extension icon is clicked
chrome.action.onClicked.addListener((tab) => {
  if (tab.url && tab.url.startsWith(CHAT_ORIGIN)) {
    chrome.sidePanel.open({tabId: tab.id}).catch(error => console.error('Error opening side panel:', error));
  } else {
    console.log('Not on the ChatGPT page');
  }
});

Общий подход к реализации sidepanel.html такой же как и для попапа: sidepanel.js может также взаимодействовать с Content Script, Chrome API, и т.д.

При этом в отличие от попапа панель автоматически не закрывается при каждом переключении фокуса с интерфейса расширения на основную страницу. Это может упростить некоторые моменты в сложных интерфейсах, связанные с удержанием состояния интерфейса и текущей информации (контекста) в нем.

Поддержка работы с боковой панелью также предлагается во фреймфорке WXT (при этом тут пишут что нужно не забыть вручную внести соответствующий пермишн в конфиг). Сам я пока что с этим фреймворком подробно не разбирался.

Примеры реализации

Вероятно, одно из самых простых и понятных применений боковой панели — это ее использование для открытия второй веб-страницы в дополнение к уже открытой «основной». В частности, это можно сделать с помощью расширения Page Sidebar. Оно в значительной мере воспроизводит функционал Split screen window в Microsoft Edge.

Split screen window на минималках
Split screen window на минималках

Одно из самых популярных расширений, использующих боковую панель (почти миллион пользователей), — это ChatGPT Sidebar. Насколько я понял, это самостоятельная интерфейсная обертка над API ChatGPT минимально связанная с содержимым основной страницы. Удобство в том что можно открыть чат не переключаясь с текущей страницы в другую вкладку, а также в том что в бесплатной версии предоставляется возможность сделать до 30 запросов к 4o-mini, включая веб-доступ и работу с картинками и PDF-файлами. Единственная связь межу основной страницей и чатом в боковике, которую я обнаружил, заключается в следующем: Если на основной странице начать печатать какой-то текст в текстовом поле, то появляется всплывающее меню, в котором можно выбрать различные действия такие как "Improve writing", выбор которых перебрасывает текст из основного окна в чат в боковике и добавляет к нему промпт, соответствующий выбранному действию. Про похожее по функционалу расширение на основе попапов ранее была статья на Хабре.

По аналогичному принципу устроены и некоторые другие расширения, размещающие функционал популярных приложений (таких как e-mail или календарь) в боковой панели.

Также есть смысл упомянуть пример из документации Google: прототип словаря, который позволяет выделить слово на странице и через контекстное меню отобразить его определение в боковой панели (реального словаря в этом прототипе под капотом нет - работает только для двух слов):

Картинка с github.com/GoogleChrome/chrome-extensions-samples
Картинка с github.com/GoogleChrome/chrome-extensions-samples

Кроме того, есть примеры расширений работающих только с API Хрома и не затрагивающих страницу, открытую в основной вкладке. Например, это расширение Tab Shelf. С его помощью можно отображать список текущих открытых вкладок браузера в формате вертикального списка.

Custom Instructions Library

На мой взгляд, основной потенциал «панельных» расширений заключается в том что бы с их помощью расширять и дополнять функционал конкретных сайтов (веб-приложений). На Хабре есть несколько статей, в которых авторы описывают такого типа расширения (например: раз, два, три, четыре).

Попробую продемонстрировать эту идею на примере функционала ChatGPT Custom Instructions. Небольшое введение в Custom Instructions я публиковал около года назад (+ есть документация). Повторю кратко Custom Instructions — это «фоновые» настройки чата, которые применяются ко всем диалогам и состоят из двух текстовых элементов (до 1500 знаков каждый): «Обо мне» и «Как нужно отвечать». Таким образом, ChatGPT при каждом обращении принимает во внимание не только то что пользователь пишет в чате, но одновременно и то что задано в Custom Instructions. Насколько я понимаю, Custom Instructions до некоторой степени является аналогом «системного промпта» при работе через API.

Так вот, по моим наблюдениям Custom Instructions могут и должны быть разными в разных сценариях использования ChatGPT:

  • Если я использую ChatGPT для профессиональных целей, то настройка «Обо мне» должна содержать информацию из моего резюме

  • Если цель — поиск кулинарных рецептов, то в «Обо мне» должно быть сказано о моих пищевых предпочтениях, на что у меня аллергия и т. п.

  • Для целей изучения иностранного языка в «Обо мне» должна быть заполнена информация о моем текущем уровне, о моих целях и приоритетах при изучении этого языка

  • и т. д.

Сам по себе ChatGPT такой множественности вариантов не предусматривает. Нужно где-то отдельно держать блокнот с разными вариантами Custom Instructions и вручную их копипастить. Собственно, «продуктовая идея» моего расширения Custom Instructions Library заключается в том что бы оптимизировать эти манипуляции. Предполагается что на странице расширения в боковой панели будут храниться разные варианты «инструкций» и их копирование в/из ChatGPT будет требовать минимума телодвижений.

В частности, в него можно добавлять новые блоки (Add New Instruction), каждый из которых состоит из заголовка и двух упомянутых выше элементов Custom Instructions: «Обо мне» и «Как нужно отвечать». Последние можно либо заполнять вручную непосредственно в соответствующих текстовых полях, либо скопировать из текущего открытого окна Custom Instructions (кнопкой Update Library from ChatGPT). Все изменения во всех текстовых полях расширения автоматически сохраняются. Ранее сохраненные «инструкции» можно скопировать с помощью второй кнопки Copy CI to ChatGPT в обратном направлении — из расширения в открытое окно Custom Instructions ChatGPT.

На мой взгляд, такой подход в полной мере задействует все возможности:

  • развитой интерфейс «панельного» расширения

  • хранение данных, не предусмотренное в самом веб-приложении

  • двустороннее взаимодействие между веб-приложением (в моем случае ChatGPT) и расширением.

Заключение

Лично я для себя вижу цель в том что бы сформировать навыки быстрой «разработки» расширений, сопоставимых по сложности с описанным выше. Под «быстрой» имеется ввиду «меньше чем за час». А «разработка» — это использование чата/композера Курсора (видимо, в сочетании с новым Claude 3.5 Sonnet).

На первый взгляд кажется что такой навык может быть достаточно полезным в различных рабочих ситуациях (по аналогии с тем как это было описано в одной из статей про Tampermonkey).

Ну как вам и#дейка?

Комментарии (1)


  1. isumix
    31.10.2024 10:01

    По идее devtools также в сайд-панели.