Ваше расширение VS Code на домашнем экране вашей любимой IDE


Узнайте, как превратить ваше расширение VS Code в виджет Marquee

Marquee — это расширение VS Code, которое приносит полностью расширяемый домашний экран прямо в вашу любимую IDE для минимального переключения контекста. Кроме того, это расширение с открытым исходным кодом. Самое интересное, что вы можете интегрировать свое расширение VS Code в нашу экосистему виджетов Marquee. Хотя Marquee фокусируется на инструментах для повышения производительности, у нас есть место и для большего веселья.

Это руководство научит вас, как превратить расширение VS Code в пользовательский виджет Marquee. В этом примере будет использовано популярное расширение vscode-spotify, и оно станет пользовательским виджетом для приборной панели Marquee. Именно его мы и будем создавать:

Немного терминологии, прежде чем мы начнем:

  1. Extension Host — это место, где запускается расширение (неотъемлемая часть VS Code). Большая часть вашей бизнес-логики будет находиться здесь.
    1. Обычно этот файл называется extension.ts.
  2. Webview — среда, в которой находится приборная панель Marquee. API VS Code, который по сути является браузерной оболочкой (iframe) внутри VS Code.
  3. Extension Development Host — представление отладчика. Так мы можем увидеть виджет в процессе разработки.

Необходимые условия

  1. VS Code
  2. Marquee
  3. vscode-spotify

Если вы хотите пройти путь с чистого листа, вы можете проверить:

https://github.com/stateful/vscode-spotify/tree/custom-widget-tutorial-start

💡 Пока вы здесь, мы могли бы похлопать в ладоши по готовящемуся PR, чтобы сделать Spotify доступным для вас в Marquee, пожалуйста: https://github.com/ShyykoSerhiy/vscode-spotify/pull/182.

Как запустить

Нажмите F5 для запуска отладчика или Run and Debug из палитры команд VS Code. Кроме того, Extension Development Host не обновляется при сохранении, как в обычном браузере при работе над react-приложением, поэтому обязательно обновляйте extension development host после каждого сохранения. Чтобы обновить command + r / crl + r или Reload Window из палитры команд.

Зависимости

В дополнение к уже установленным пакетам, установите эти зависимости, которые будут использоваться для создания нашего виджета.

  1. tangle@2.1.0 — канал связи, если мы хотим отправлять информацию для отображения с хоста расширения в веб-вью. Tangle обеспечивает лучший интерфейс связи x-sandbox, чем нанизывание postMessage/onMessage на несколько процессов.
  2. lit@2.2.8 — используются для создания веб-вью, виджеты marquee не привязаны к какой-либо библиотеке или фреймворку фронтенда, но в конечном итоге должны быть экспортированы как веб-компонент. Для простоты мы будем использовать lit.html.
  3. fortawesome/free-solid-svg-icons (иконка для отображения в списке виджетов).
  4. webpack@5.74.0 (зависимость от разработчика).
  5. webpack-cli@4.10.0 (dev dependency).
  6. npm-run-all@4.1.5 (dev dependency).

Хост расширений

Хост расширений отвечает за запуск расширений в соответствии с документацией https://code.visualstudio.com/api/advanced-topics/extension-host VS Code.

Узел расширения будет иметь функциюactivate, которая запускает расширение.

Для vscode-spotify узел расширения выглядит следующим образом.

/src/extension.ts

import { ExtensionContext, window } from 'vscode';
...
import { getStore } from './store/store';

// This method is called when your extension is activated. Activation is
// controlled by the activation events defined in package.json.
export function activate(context: ExtensionContext) {
    // This line of code will only be executed once when your extension is activated.
    registerGlobalState(context.globalState);
    getStore(context.globalState);
    const spotifyStatus = new SpotifyStatus();
    const controller = new SpotifyStatusController();
    const playlistTreeView = window.createTreeView('vscode-spotify-playlists', { treeDataProvider: new TreePlaylistProvider() });
    const albumTreeView = window.createTreeView('vscode-spotify-albums', { treeDataProvider: new TreeAlbumProvider() });
    const treeTrackProvider = new TreeTrackProvider();
    const trackTreeView = window.createTreeView('vscode-spotify-tracks', { treeDataProvider: treeTrackProvider });
    treeTrackProvider.bindView(trackTreeView);
    // Add to a list of disposables which are disposed when this extension is deactivated.
    context.subscriptions.push(connectPlaylistTreeView(playlistTreeView));
    context.subscriptions.push(connectAlbumTreeView(albumTreeView));
    context.subscriptions.push(connectTrackTreeView(trackTreeView));
    context.subscriptions.push(controller);
    context.subscriptions.push(spotifyStatus);
    context.subscriptions.push(playlistTreeView);
    context.subscriptions.push(createCommands(SpoifyClientSingleton.spotifyClient));
}
Вход в полноэкранный режим Выход из полноэкранного режима

Нам нужно создать канал, который позволит нашему хосту расширения взаимодействовать с webview. В отличие от работы в обычном браузере, webview по сути являются iFrame, содержащими подмножество api браузера, не позволяя импортировать файлы и другие возможности. Мы будем использовать tangle для передачи данных от нашего хоста расширения к нашему webview, который мы создадим позже.

Данные, которые нам нужно передать нашему webview, находятся внутри функций getStore и getState (нам нужно импортировать их внутрь). Обратите внимание, что хост расширения работает на setTimeout, который запускается каждую секунду, обновляя состояние. Данные возвращают информацию, которую нам нужно отобразить, такую как:

  1. трек
  2. playerState
  3. loginState
  4. isRunning

Настройка tangle на хосте расширения:

Чтобы настроить наш интерфейс marquee, мы вернем интерфейс marquee:

Note: Никакой код не был удален, просто используем ..., чтобы сфокусироваться на новом добавленном коде.

...
import { Client } from 'tangle';
import { ILoginState, IPlayerState, ITrack } from './state/state';
import { getState } from "./store/store";

// This method is called when your extension is activated. Activation is
// controlled by the activation events defined in package.json.
export function activate(context: ExtensionContext) {
    // This line of code will only be executed once when your extension is activated.
    ...
    context.subscriptions.push(createCommands(SpoifyClientSingleton.spotifyClient));
    return {
        marquee: {
          setup: (
            tangle: Client<{
              track: ITrack;
              playerState: IPlayerState;
              loginState: ILoginState | null;
              isRunning: boolean
            }>
          ) => {
            return tangle.whenReady().then(() => {
              getStore().subscribe(() => {
                const { track, playerState, loginState, isRunning } = getState();
                tangle.emit("isRunning", isRunning);
                tangle.emit("loginState", loginState);
                tangle.emit("track", track);
                tangle.emit("playerState", playerState);
              });
            });
          }
        },
      };
}
Вход в полноэкранный режим Выход из полноэкранного режима

Пояснение

  1. Мы экспортируем интерфейс marquee с функцией настройки, которая испускается после события активации нашего расширения в Marquee ожидает пользовательский виджет.
    1. Ссылка — https://www.npmjs.com/package/tangle
...
context.subscriptions.push(createCommands(SpoifyClientSingleton.spotifyClient));
return {
  marquee: {
        setup: (
        tangle: Client<{
          track: ITrack;
            playerState: IPlayerState;
          loginState: ILoginState | null;
        isRunning: boolean
       }>
             ) => {
         return tangle.whenReady().then(() => {
             getStore().subscribe(() => {
           const { track, playerState, loginState, isRunning } = getState();
           tangle.emit("isRunning", isRunning);
           tangle.emit("loginState", loginState);
           tangle.emit("track", track);
           tangle.emit("playerState", playerState);
         });
        });
       }
      },
       };

Вход в полноэкранный режим Выход из полноэкранного режима

webpack

  1. Создайте файл webpack.config.ts в корневом каталоге.
  2. Он будет тем, что мы используем в качестве ссылки на наш widget.
  3. Таким образом, конечный пакет javascript будет содержать только то, что необходимо, и иметь минимальный размер.
import * as path from "path";
import { Configuration } from "webpack";
const widgetConfig: Configuration = {
  target: "node",
  entry: path.resolve(__dirname, "src", "marquee/widget.ts"),
  output: {
    path: path.resolve(__dirname, "out", "marquee"),
    filename: "widget.js",
    libraryTarget: 'commonjs2'
  },
  devtool: "source-map",
  externals: {
    vscode: "commonjs vscode"
  },
  module: {
    rules: [
      {
        test: /.ts$/,
        exclude: /node_modules/,
        use: [
          {
            loader: "ts-loader",
            options: {
              compilerOptions: {
                declaration: false,
                declarationMap: false,
                rootDir: __dirname,
              },
            },
          },
        ],
      },
      {
        test: /.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: "file-loader",
          },
        ],
      },
    ],
  },
};

export default widgetConfig;
Вход в полноэкранный режим Выход из полноэкранного режима

package.json

Чтобы настроить marquee, нам нужно иметь возможность настроить наше окружение

  1. Marquee будет искать в расширении package.json, чтобы узнать, куда направлен наш виджет marquee. Мы можем сделать это следующим образом:

    {
      "name": "vscode-spotify",
      "description": "Use Spotify inside vscode.",
      "version": "3.2.1",
      "publisher": "shyykoserhiy",
      "license": "MIT",
      "engines": {
        "vscode": "^1.49.0"
      ...
      "marquee": {
        "widget": "/out/marquee/widget.js"
      },
    ....
    }
    
  2. Нам также нужно настроить команды watch и compile.

    1. Мы будем использовать команду npm-run-all, чтобы помочь нам одновременно запустить команды tsc и webpack. tsc создает хост расширения, а webpack создает веб-вью. Цели у обоих окружений разные, поэтому здесь проще разделить задачи.
    "compile": "run-p compile:*",
    "compile:ts": "tsc --project ./",
    "compile:webpack": "webpack --mode production",
    "watch": "run-p watch:*",
    "watch:ts": "tsc --watch --project ./",
    "watch:webpack": "webpack --mode development --watch",
    

    Если вы ранее запускали команду npm run watch, вам нужно будет перезапустить ее.

tsconfig.json

Нам нужно исключить новые файлы, предназначенные для marquee webview, такие как webpack и /marquee/*, поскольку они важны для разработки, но не нужны для упаковки.

{
    "compilerOptions": {
        ...
    },
    "exclude": [
        "node_modules",
        ".vscode-test",
        "webpack.config.ts",
        "./src/marquee/*.ts"
    ]
}
Вход в полноэкранный режим Выход из полноэкранного режима

Виджет Marquee

Мы настроили marquee со стороны хоста расширения, теперь мы можем приступить к созданию нашего виджета. 🙌

  1. Создайте новую папку в папке src и назовите ее marquee
    1. Примечание — путь должен быть путем вывода вашего виджета, заданным в webpack.config.ts при ссылке на ваш входной файл.
  2. Создайте types.ts — некоторые типы Marquee, которые вы можете использовать.

    import type { Webview } from "vscode";
    
    interface VSCodeWebview extends Webview {
      getState: () => any;
      setState: (param: any) => void;
    }
    
    export interface ThirdPartyWidgetOptions {
      name: string;
      icon: any;
      label: string;
      tags: string[];
      description: string;
    }
    
    export interface MarqueeInterface {
      defineWidget: (
        widgetOptions: ThirdPartyWidgetOptions,
        constructor: CustomElementConstructor,
        options?: ElementDefinitionOptions
      ) => void;
    }
    
    export interface MarqueeWindow extends Window {
      marqueeExtension: MarqueeInterface;
      vscode: VSCodeWebview;
    }
    
  3. Создайте widget.ts.

    1. настройка для виджета marquee и канала связи tangle, чтобы мы могли слушать наш хост расширения.

      import * as Channel from "tangle/webviews";
      
      declare const window: MarqueeWindow;
      
      // @ts-expect-error missing "esModuleInterop" in tsconfig
      const ch = new Channel<{
        track: ITrack;
        playerState: IPlayerState;
        loginState: ILoginState | null;
        isRunning: boolean;
      }>("shyykoserhiy.vscode-spotify"); //this is whatever the extension id is
      const client = ch.attach(window.vscode);
      
    2. Использование window.marqueeExtension.defineWidget({...}) создаст виджет

      window.marqueeExtension.defineWidget(
        {
          name: "marquee-spotify",
          icon: faMusic,
          label: "Spotify",
          tags: ["productivity"],
          description: "Extension of VS Code Spotify",
        },
        StatefulMarqueeWidget //class made using lit.html
      );
      

    c. Поскольку для создания виджета вы можете использовать любой UI-фреймворк, эта часть может варьироваться. Просто помните, что мы экспортируем web-компонент. В этом учебнике мы используем lit.html (это простой и соответствующий стандартам вариант) для создания нашего веб-компонента.

    1. Используя клиент tangle, мы можем слушать события, которые исходят от нашего узла расширения. Каждый раз, когда tangle.emit срабатывает от узла расширения, мы можем обновить состояние нашего свойства здесь.
    class StatefulMarqueeWidget extends LitElement {
    
        static styles = css`
        //css goes here
        `
        // Reactive properties are properties that can trigger the reactive update cycle when changed, re-rendering the component, and optionally be read or written to attributes. 
        @property()
        track: ITrack | undefined;
      isRunning: boolean | undefined;
    
        constructor(){
            super();
            // gets thedetails of the current song/track that is playing
            client.on("track", (track: ITrack) => {
                this.track = track;
            })
            // checks if the spotify application is running
            client.on("isRunning", (isRunning: boolean) => {
          this.isRunning = isRunning;
        });
            //tracks the pause/play state
        client.on("playerState", (state: IPlayerState) => {
          if (state.state === "paused") {
            //@ts-ignore
            this.shadowRoot?.getElementById("pausePlayIcon")?.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 449 512" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.494 0.768586C10.635 2.85559 5.366 7.26059 1.864 13.8346C0.0599984 17.2216 0 25.0456 0 255.835V494.335L2.158 498.335C7.839 508.863 20.604 514.298 31 510.615C37.108 508.452 437.525 277.031 441.338 273.46C446.716 268.424 448.926 263.291 448.926 255.835C448.926 248.379 446.717 243.249 441.338 238.207C437.789 234.882 37.439 3.35759 31.5 1.19559C27.627 -0.213414 21.351 -0.404414 17.494 0.768586Z" fill="white"/></svg>`;
          } else if (state.state === "playing") {
            //@ts-ignore
            this.shadowRoot?.getElementById("pausePlayIcon")?.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 313 512" fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M43.3305 1.30915C34.2275 3.28715 23.5735 9.27315 16.3775 16.4532C9.2415 23.5712 2.8545 35.6231 1.1235 45.2321C-0.3745 53.5441 -0.3745 458.052 1.1235 466.364C1.7115 469.628 3.9935 476.052 6.1965 480.639C15.2135 499.425 34.9255 511.715 56.1445 511.781C63.2855 511.803 75.7245 508.629 82.3795 505.087C89.8335 501.119 101.128 489.581 104.926 482.055C111.491 469.043 111.054 485.184 111.054 255.798C111.054 26.4122 111.491 42.5532 104.926 29.5412C101.128 22.0152 89.8335 10.4772 82.3795 6.50915C71.0675 0.488151 56.2125 -1.48985 43.3305 1.30915ZM242.246 1.82315C238.225 2.87615 232.6 4.98115 229.746 6.50115C222.404 10.4092 210.981 22.0142 207.307 29.2982C200.546 42.7002 201.055 24.2502 201.055 255.798C201.055 487.346 200.546 468.896 207.307 482.298C210.978 489.576 222.402 501.187 229.729 505.087C251.345 516.592 278.304 512.531 295.732 495.143C302.868 488.025 309.255 475.973 310.986 466.364C312.484 458.052 312.484 53.5441 310.986 45.2321C310.398 41.9681 308.116 35.5442 305.913 30.9572C294.587 7.36215 267.834 -4.87985 242.246 1.82315Z" fill="white"/></svg>`;
          }
          if (state.isShuffling) {
            this.shadowRoot!.getElementById("shuffleActive")!.style.display = "block";
          } else {
            this.shadowRoot!.getElementById("shuffleActive")!.style.display = "none";
          }
          if (state.isRepeating) {
            this.shadowRoot!.getElementById("repeatActive")!.style.display = "block";
          } else {
            this.shadowRoot!.getElementById("repeatActive")!.style.display = "none";
          }
        });
        }
        render() {
            // html goes here
        }
    }
    

    this.track теперь должен показывать информацию о треке текущей проигрываемой песни.

    d. Используйте render для отображения html-файла.

    render() {
            if (!this.track) {
          return html`
            <div class="defaultWrapper">
              <div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
            </div>
          `;
        }
        if (!this.isRunning){
          return html`
            <div class="defaultWrapper">
              <div class="wrapper-message">You must have Spotify Win/Mac App installed on and running to display information. This extension requires Spotify Premium to work on Windows.</div>
            </div>
          `;
        }
            return html`
          <div class="img-wrapper">
            <img
              id="trackArtwork"
              class="${this.isHoveringControls ? "trackArtwork-darkened" : "trackArtwork"}"
              src="${this.track.artwork_url}"
            />
            <div 
              class="name-controller-wrapper" 
              @mouseover="${() => this.isHoveringControls = true}" 
              @mouseleave="${() => this.isHoveringControls = false}"
            >
              <div class="name-wrapper">
                <p>${this.track.name}</p>
                <p>${this.track.artist}</p>
              </div>
              <section class="controller">
                <div class="shuffle-repeat-btn">
                  <button
                    class="shuffle"
                    @click="${() =>
                      this._triggerSpotifyCommand("spotify.toggleShuffling")}"
                  >
                    <svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 449 448" fill="none">
                      <path fill-rule="evenodd" clip-rule="evenodd" d="M341.717 1.47365C336.157 4.49065 335.954 5.87565 335.954 40.8396V72.8706L333.204 73.4326C312.675 77.6306 298.928 83.1506 284.104 93.1476C268.401 103.738 265.496 107.279 189.949 207.909C148.892 262.598 115.946 305.555 112.854 308.431C106.151 314.663 99.1392 319.161 90.9132 322.504C79.9082 326.975 71.9852 327.909 45.0442 327.909C15.7892 327.909 13.3872 328.409 6.65522 335.897C0.30422 342.961 -1.44778 350.433 1.17022 359.293C2.79322 364.786 11.0772 373.07 16.5702 374.693C22.6732 376.496 68.7452 376.313 80.9542 374.438C100.044 371.505 115.99 365.344 131.39 354.952C147.574 344.032 150.131 340.92 224.732 241.409C263.49 189.709 297.508 144.992 300.328 142.037C308.83 133.128 322.272 125.152 333.204 122.53L335.954 121.871V152.44C335.954 186.19 336.152 187.408 342.126 190.498C349.344 194.231 348.155 195.2 400.173 143.19L447.954 95.4166L403.204 50.5616C378.591 25.8916 356.699 4.40165 354.554 2.80765C350.204 -0.426354 346.019 -0.860353 341.717 1.47365ZM15.9542 73.1626C11.0062 74.7976 2.66522 83.4646 1.17022 88.5246C-1.36878 97.1166 0.275221 104.721 6.10422 111.342C13.0312 119.212 13.5952 119.331 46.9542 120.003C80.1112 120.67 82.8572 121.115 96.7772 128.074C109.337 134.353 116.566 141.847 138.454 171.281C148.904 185.333 157.679 196.83 157.954 196.831C158.916 196.834 187.452 157.748 187.189 156.788C186.546 154.438 155.954 114.743 150.024 108.564C133.945 91.8106 112.738 80.0456 88.2782 74.3086C79.4802 72.2446 21.5132 71.3266 15.9542 73.1626ZM242.954 270.523C234.979 281.269 228.579 290.513 228.731 291.065C229.564 294.089 262.779 336.445 268.69 342.021C287.128 359.414 305.869 368.773 333.704 374.485L335.954 374.947L335.958 407.178C335.961 436.947 336.105 439.644 337.839 442.489C338.872 444.183 341.158 446.165 342.919 446.895C349.771 449.733 349.651 449.832 400.575 398.972L448.071 351.535L404.263 307.624C380.168 283.473 358.276 261.957 355.614 259.811C350.291 255.519 346.788 254.909 341.921 257.426C336.176 260.397 335.954 261.807 335.954 295.378V325.947L333.204 325.294C328.132 324.09 318.741 319.806 312.411 315.808C303.597 310.241 296.048 301.708 275.954 274.598C266.329 261.612 258.229 250.987 257.954 250.986C257.679 250.986 250.929 259.777 242.954 270.523Z" fill="white" />
                    </svg>
                  </button>
                  <div id="shuffleActive">.</div>
                </div>
                <button
                  class="prevBtn"
                  @click="${() => this._triggerSpotifyCommand("spotify.previous")}"
                >
                  <svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 488 512" fill="none">
                    <path fill-rule="evenodd" clip-rule="evenodd" d="M18.517 2.29534C14.455 3.97834 11.019 6.39234 7.87901 9.76934C-0.741987 19.0403 0.0170136 -4.71869 0.0170136 256.002C0.0170136 485.214 0.0390133 487.845 2.01401 493.123C4.36901 499.419 10.027 505.491 16.728 508.914C21.134 511.165 22.637 511.361 35.517 511.361C48.13 511.361 50.04 511.125 54.796 508.975C61.778 505.82 68.547 498.286 70.531 491.462C71.852 486.915 72.003 458.241 71.775 253.826L71.517 21.3613L69.403 16.8613C66.748 11.2103 61.082 5.56634 55.117 2.62934C51.177 0.690343 48.652 0.325343 37.517 0.0873429C25.592 -0.167657 24.021 0.015343 18.517 2.29534ZM447.017 0.492343C440.173 1.63134 434.848 3.47634 428.06 7.06234C415.461 13.7183 130.543 210.523 123.507 217.43C105.336 235.268 101.117 259.068 112.351 280.361C119.452 293.82 119.526 293.875 274.349 400.399C354.24 455.367 422.478 501.636 427.849 504.479C451.968 517.245 472.467 512.64 482.155 492.278C488.313 479.336 488.02 491.164 487.982 256.523C487.944 25.0873 488.131 33.3433 482.668 21.4113C476.552 8.05134 468.112 1.79334 454.517 0.538343C451.767 0.284343 448.392 0.264343 447.017 0.492343Z" fill="white" />
                  </svg>
                </button>
                <button
                  class="${this.isHoveringControls ? "pausePlay-hovered" : "pausePlay-default"}"
                  id="pausePlayIcon"
                  @click="${() => this._triggerSpotifyCommand("spotify.playPause")}"
                >
                </button>
                <button
                  class="nextBtn"
                  @click="${() => this._triggerSpotifyCommand("spotify.next")}"
                >
                  <svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 460 512" fill="none" class="icons-svg">
                    <path fill-rule="evenodd" clip-rule="evenodd" d="M16.463 1.46632C10.597 3.54932 5.546 8.13832 2.59 14.0703L0 19.2683V255.875V492.482L2.59 497.68C8.056 508.648 20.312 514.151 32 510.886C33.925 510.348 119.425 462.7 222 405.002L408.5 300.095L409 396.235C409.467 486.052 409.618 492.638 411.301 496.375C420.185 516.109 447.836 516.892 457.41 497.68L460 492.482V255.875V19.2683L457.41 14.0703C447.836 -5.14168 420.185 -4.35868 411.301 15.3753C409.618 19.1123 409.467 25.6983 409 115.515L408.5 211.655L222 106.748C119.425 49.0503 33.925 1.40232 32 0.864318C27.205 -0.474682 21.285 -0.245682 16.463 1.46632Z" fill="white" />
                  </svg>
                </button>
                <div
                  class="shuffle-repeat-btn"
                  @click="${() =>
                    this._triggerSpotifyCommand("spotify.toggleRepeating")}"
                >
                  <button class="repeat">
                    <svg xmlns="http://www.w3.org/2000/svg" width="15" height="100%" viewBox="0 0 512 472" fill="none">
                      <path fill-rule="evenodd" clip-rule="evenodd" d="M349.422 1.1099C338.616 4.8869 333.099 17.1429 337.46 27.6849C338.408 29.9779 345.014 37.4389 354.698 47.1529L370.399 62.9029L234.41 62.9059C143.85 62.9079 95.4153 63.2669 89.4223 63.9819C48.3913 68.8739 14.3593 98.7589 3.10129 139.784L0.461288 149.403L0.131288 228.903C-0.215712 312.314 -0.178712 313.13 4.19429 319.281C7.22129 323.538 16.0173 327.265 21.5633 326.64C27.6483 325.954 34.7493 321.344 37.3313 316.403C39.3883 312.469 39.4313 311.096 39.9413 232.903C40.4213 159.408 40.5983 152.988 42.2873 147.903C49.1933 127.118 64.0703 112.236 84.9223 105.254C90.1433 103.506 97.5113 103.398 230.362 103.134L370.302 102.856L353.3 120.129C338.507 135.157 336.126 138.021 334.978 142.157C333.235 148.442 334.137 154.314 337.612 159.319C343.073 167.182 353.836 170.059 362.357 165.935C364.987 164.661 378.837 151.507 401.576 128.685C438.55 91.5759 438.942 91.0899 438.894 82.4029C438.855 75.1089 435.698 71.2809 401.621 37.2039C367.029 2.6119 363.67 -0.122104 355.922 0.00389602C353.997 0.034896 351.072 0.532897 349.422 1.1099ZM484.969 146.208C480.266 147.707 474.634 153.584 473.148 158.543C472.227 161.617 471.922 181.41 471.922 238.109C471.922 309.953 471.827 313.954 469.95 321.294C464.427 342.889 448.939 359.18 426.922 366.552C421.701 368.3 414.333 368.408 281.482 368.672L141.542 368.95L158.544 351.677C173.337 336.649 175.718 333.785 176.866 329.649C181.519 312.871 165.018 298.539 149.422 305.812C146.773 307.048 133.547 319.624 110.268 343.04C73.3053 380.223 72.9023 380.724 72.9503 389.403C72.9903 396.736 76.0913 400.482 110.718 435.019C139.042 463.268 145.179 468.916 149.164 470.401C161.181 474.88 174.466 466.707 175.678 454.09C176.536 445.158 175.081 442.831 157.584 425.153L141.501 408.903H277.488C365.634 408.903 416.456 408.537 421.948 407.862C444.976 405.033 465.436 395.073 481.764 378.745C494.801 365.708 503.752 350.187 508.771 331.914L511.383 322.403L511.702 242.903C511.946 182.228 511.73 162.354 510.791 158.973C507.875 148.47 496.113 142.655 484.969 146.208Z" fill="white" />
                    </svg>
                  </button>
                  <div id="repeatActive">.</div>
                </div>
              </section>
            </div>
          </div>
        `;
        }
    

Теперь обновите расширение-хост, и у нас должно получиться что-то вроде этого. Но помните, что вы можете придумать и другие способы настройки отображения. Просто поиграйте с HTML и сделайте свою собственную версию виджета vscode-spotify.

Надеюсь, это руководство пробудило ваш интерес к созданию собственного виджета для Marquee. У нас также есть документация по созданию виджетов Marquee здесь. Кроме того, если вам нужна помощь в интеграции вашего расширения или у вас есть вопросы, вы можете связаться с нами на нашем канале Discord здесь: https://discord.com/invite/BQm8zRCBUY.

Ссылки / Дополнительная документация

документация по marquee

документация по tangle

https://discord.com/invite/BQm8zRCBUY

световые документы

Оцените статью
devanswers.ru
Добавить комментарий