Hello, world!
В этом небольшом туториале мы с вами разработаем простое, но полезное расширение для браузера с помощью Plasmo.
Наше расширение будет представлять собой вызываемый сочетанием клавиш попап с инпутом для поиска информации на MDN с выводом 5 лучших результатов в виде списка. Кроме основного функционала, мы добавим страницу настроек для кастомизации цветов и отображения хлебных крошек. Мы будем разрабатывать расширения для Chrome, которое также будет работать в Firefox.
Вот как это будет выглядеть:
Для тех, кого интересует только код, вот ссылка на соответствующий репозиторий.
Интересно? Тогда прошу под кат.
Основной функционал — попап с поиском
Для работы с зависимостями будет использоваться Yarn.
Создаем шаблон приложения:
# mdn-finder - название приложения/расширения yarn create plasmo mdn-finder
Переходим в созданную директорию и устанавливаем зависимости:
cd mdn-finder yarn
Устанавливаем дополнительные зависимости, необходимые для работы поиска:
yarn add @plasmohq/storage downshift flexsearch fzf swr
- @plasmohq/storage — абстракция над Storage API, который может использоваться расширениями браузера для локального хранения данных;
- downshift — библиотека, предоставляющая примитивы для разработки простых, гибких, отвечающих всем критериям WAI-ARIA React-компонентов autocomplete/combobox или select/dropdown;
- flexsearch — библиотека для реализации полнотекстового поиска;
- fzf — библиотека для реализации неточного (fuzzy) поиска;
- swr — хуки React для получения, кэширования и мутации данных.
Структура проекта будет следующей:
- assets - icon.png - search-index.json - search.png - src - components - Search.tsx - search - fuzzy-search.ts - search-utils.ts - search.tsx - background.ts - options.tsx - popup.tsx - storage.ts - style.css - ...
После переноса файлов в директорию src, необходимо немного отредактировать файл tsconfig.json:
{ // ... "compilerOptions": { "baseUrl": ".", "paths": { "~*": [ "./src/*" ] } } }
О самом поиске я рассказывал в этой статье, поэтому в данном туториале мы сосредоточимся на Plasmo. Скопируйте файлы из директорий components, search и assets, а также файл style.css из репозитория проекта. Поисковый индекс (search-index.json), также можно копировать с MDN. Запросы к MDN из другого источника блокируются CORS, поэтому поисковый индекс хранится локально.
Для того, чтобы иметь возможность работать с поисковым индексом, необходимо немного отредактировать файл package.json:
{ // ... "manifest": { "web_accessible_resources": [ { "resources": [ "assets/search-index.json" ], "matches": [ "https://*/*" ] } ], "host_permissions": [ "https://*/*" ] } }
Точкой входа приложения Plasmo является файл popup.tsx. Как следует из названия, этот компонент отвечает за рендеринг попапа, в котором будет находиться инпут для поиска. Редактируем этот файл следующим образом:
import Search from './components/Search' import './style.css' function IndexPopup() { return <Search preload={true} /> } export default IndexPopup
Запускаем сервер для разработки с помощью команды yarn dev. Выполнение этой команды приводит к генерации директории build/chrome-mv3-dev с файлами расширения.
Переходим по адресу chrome://extensions/ и загружаем расширение в браузер (кнопка «Загрузить распакованное расширение»/»Load unpacked extension»):
В режиме разработки расширение, загруженное в браузер, автоматически обновляется при изменении соответствующих файлов.
Сочетание клавиш для запуска расширения можно установить на странице chrome://extensions/shortcuts:
Для создания производственной сборки необходимо выполнить команду yarn build. По умолчанию создается сборка для Chrome. В настоящее время Plasmo также поддерживает создание сборок для Firefox. Команда для создания такой сборки: yarn build --target=firefox-mv2. Подробнее почитать об этом можно здесь.
Для тестирования расширения в Firefox необходимо сделать 2 вещи:
- создать в директории
srcфайлbackground.tsследующего содержания:
export {}
Этот файл предназначен для запуска скриптов, отвечающих за выполнение фоновых задач. К таким скриптам относится, например, логика сервис-воркера. Подробнее почитать об этом можно здесь. Почему-то без этого файла расширение в Firefox не запускается.
- создать производственную сборку в виде архива с помощью команды
yarn build --target=firefox-mv2 --zip.
Дополнительный функционал — страница настроек
Для инициализации страницы настроек достаточно создать файл options.tsx в директории src.
Простейшим способом обмена данными между попапом и страницей настроек (а также другими скриптами) является использование предоставляемого Plasmo хранилища.
Создаем в директории src файл storage.ts следующего содержания:
import { Storage } from '@plasmohq/storage' // ключ объекта настроек export const OPTIONS_KEY = 'mdn_finder_options' // дефолтные настройки export const defaultOptions = { // цвет фона backgroundColor: '#282c34', // цвет текста textColor: '#f7f7f7', // фон выделения selectionBackground: '#5cb85c', // цвет выделения selectionColor: '#282c34', // индикатор отображения хлебных крошек в списке результатов поиска showUrl: true } // создаем экземпляр хранилища const storage = new Storage() // и экспортируем его export default storage
Редактируем файл options.tsx следующим образом:
import { useRef } from 'react' import storage, { defaultOptions, OPTIONS_KEY } from '~storage' import './style.css' export default function IndexOptions() { // ссылка на кнопку отправки формы const btnRef = useRef<HTMLButtonElement | null>(null) // обработчик отправки формы const onSubmit: React.FormEventHandler = async (e) => { e.preventDefault() // получаем данные формы в виде объекта const formData = Object.fromEntries( new FormData(e.target as HTMLFormElement).entries(), ) try { // записываем настройки в хранилище await storage.set(OPTIONS_KEY, formData) // меняем текст кнопки if (btnRef.current) { btnRef.current.textContent = 'Saved' const id = setTimeout(() => { btnRef.current.textContent = 'Save' clearTimeout(id) }, 1000) } } catch (e) { console.log(e) } } return ( <form className='options' onSubmit={onSubmit}> <label> Background color:{' '} <input type='color' name='backgroundColor' defaultValue={defaultOptions.backgroundColor} /> </label> <label> Result item color:{' '} <input type='color' name='textColor' defaultValue={defaultOptions.textColor} /> </label> <label> Selection background:{' '} <input type='color' name='selectionBackground' defaultValue={defaultOptions.selectionBackground} /> </label> <label> Selection color:{' '} <input type='color' name='selectionColor' defaultValue={defaultOptions.selectionColor} /> </label> <label> Show URL:{' '} <input type='checkbox' name='showUrl' defaultChecked={defaultOptions.showUrl} /> </label> <button ref={btnRef}>Save</button> </form> ) }
Для того, чтобы попасть на страницу настроек, необходимо кликнуть по иконке расширения и выбрать пункт «Параметры»/»Options»:
Возвращаемся к попапу. Редактируем файл search/search.tsx. Импортируем хранилище и извлекаем из него настройки:
import storage, { defaultOptions, OPTIONS_KEY } from '~storage' // ... function InnerSearchNavigateWidget(props: InnerSearchNavigateWidgetProps) { // ... const [options, setOptions] = useState(defaultOptions) // ... useEffect(() => { storage.get<typeof options>(OPTIONS_KEY).then((opts) => { if (opts) { setOptions(opts) } }) }, []) // далее работаем с этим компонентом }
Индикатор отображения хлебных крошек (options.showUrl) используется при формировании списка результатов поиска:
resultItems.map((item, i) => ( <div {...getItemProps({ key: item.url, className: 'result-item ' + (i === highlightedIndex ? 'highlight' : ''), item, index: i, })} > <HighlightMatch title={item.title} q={inputValue} /> {/* ! */} {Boolean(options.showUrl) ? ( <> <br /> <BreadcrumbURI uri={item.url} positions={item.positions} /> </> ) : null} </div> ))
Цвет фона (options.backgroundColor) передается элементу формы:
<form // ... style={ { '--background-color': options.backgroundColor, } as React.CSSProperties } > {/* ... */} </form>
В файле style.css у нас имеются такие строки:
.search-form { --background-color: var(--dark); /* ... */ background-color: var(--background-color); }
Остальные цвета передаются контейнеру с результатами поиска:
<div className='search-results' style={ { '--text-color': options.textColor, '--selection-background': options.selectionBackground, '--selection-color': options.selectionColor, } as React.CSSProperties } > {searchResults} </div>
В style.css у нас имеются такие строки:
.search-results { --text-color: var(--light); --selection-background: var(--success); --selection-color: var(--dark); } .result-item span, .result-item small { color: var(--text-color); } .result-item mark { background-color: var(--selection-background); color: var(--selection-color); }
Спасибо переменным CSS за их динамичность 🙂
Меняем настройки:
Запускаем расширение:
Видим, что настройки благополучно применяются к попапу.
Следует отметить, что проект, созданный с помощью Plasmo CLI, включает в себя GitHub Action Browser Platform Publisher для автоматической публикации расширения во всех поддерживаемых сторах. Подробнее почитать об этом можно здесь. Соответствующий файл можно найти в директории .github/workflows.
К слову, поисковый индекс со статьями на русском языке можно найти здесь.
Надеюсь, вы узнали что-то новое и не зря потратили время.
Happy coding!
ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/720646/
Добавить комментарий