adjust-figma-toolbelt

adjust-figma-toolbelt is a versatile script designed to enhance your Figma experience by allowing you to reposition the toolbelt with ease.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

/* eslint-disable */
// ==UserScript==
// @name              adjust-figma-toolbelt
// @name:zh-CN        adjust-figma-toolbelt
// @namespace         https://github.com/palmcivet/adjust-figma-toolbelt
// @version           1.0.6
// @license           MIT
// @author            Palm Civet
// @description       adjust-figma-toolbelt is a versatile script designed to enhance your Figma experience by allowing you to reposition the toolbelt with ease.
// @description:zh-CN adjust-figma-toolbelt 是一个用于增强 Figma 体验的脚本,可以轻松地将工具栏调整到想要的位置。
// @match             https://www.figma.com/design/*
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_addStyle
// @grant             GM_registerMenuCommand
// @grant             GM_unregisterMenuCommand
// ==/UserScript==
(function() {
  "use strict";
  // @license           MIT
  const PREFIX = "adjust";
  const STORAGE_LEFT = `__${PREFIX.toUpperCase()}_LEFT__`;
  const STORAGE_BOTTOM = `__${PREFIX.toUpperCase()}_BOTTOM__`;
  const TOOLBELT_SELECTOR = `[class*="positioned_design_toolbelt--root--"]`;
  const FLOAT_MENU_STYLE = `
${TOOLBELT_SELECTOR} [class*="dropdown--dropdown--"] {
  margin-top: 6px;
  bottom: unset !important;
}

${TOOLBELT_SELECTOR} [class*="scroll_container--clipContainer--"] {
  height: auto !important;
}

${TOOLBELT_SELECTOR} [class*="pointing_dropdown--scrollIndicator--"] {
  display: none !important;
}`;
  const DEFAULT_PADDING = 12;
  const DEFAULT_TOP = 48 + DEFAULT_PADDING;
  const FLOAT_MENU_TOP = 184;
  const LABEL_PIN_DEFAULT_TOP = "⬆️ Pin the toolbelt to the top";
  const LABEL_UNPIN_DEFAULT_TOP = "🔝 The toolbelt is already at the top";
  const LABEL_START_CUSTOM_POSITION = "🎨 Customize the toolbelt position";
  const LABEL_STOP_CUSTOM_POSITION = "💾 Save the position and exit";
  // @license           MIT
  function findToolbeltElement() {
    const element = window.document.querySelector(TOOLBELT_SELECTOR);
    if (!element) {
      return null;
    }
    return element;
  }
  // @license           MIT
  async function getToolbeltPosition() {
    const left = await GM.getValue(STORAGE_LEFT);
    const bottom = await GM.getValue(STORAGE_BOTTOM);
    console.info(`[${PREFIX}] Read persistent data: ${left}, ${bottom}.`);
    return {
      left: left ?? null,
      bottom: bottom ?? DEFAULT_PADDING
    };
  }
  async function setToolbeltPosition({
    left,
    bottom
  }) {
    console.info(`[${PREFIX}] Save persistent data: ${left}, ${bottom}.`);
    await GM.setValue(STORAGE_LEFT, left);
    await GM.setValue(STORAGE_BOTTOM, bottom);
  }
  // @license           MIT
  function createMenuStore() {
    const menuStore2 = /* @__PURE__ */ new Map();
    const _removeMenus = () => {
      for (const menu of menuStore2.values()) {
        GM_unregisterMenuCommand(menu.id);
      }
    };
    const _registerMenus = () => {
      for (const menu of menuStore2.values()) {
        const newId = GM_registerMenuCommand(menu.label, menu.callback);
        menu.id = newId;
      }
    };
    const update = (type, label, callback) => {
      _removeMenus();
      menuStore2.set(type, {
        id: 0,
        label,
        callback
      });
      _registerMenus();
    };
    return {
      update
    };
  }
  function createFloatMenuStore() {
    let styleElement;
    const update = (element) => {
      setTimeout(() => {
        const { top, bottom } = element.getBoundingClientRect();
        const toolbeltBottom = window.innerHeight - bottom;
        console.log(top, FLOAT_MENU_TOP, toolbeltBottom, FLOAT_MENU_TOP);
        if (top < FLOAT_MENU_TOP && toolbeltBottom > FLOAT_MENU_TOP) {
          styleElement = GM_addStyle(FLOAT_MENU_STYLE);
        } else {
          styleElement == null ? void 0 : styleElement.remove();
        }
      }, 1e3);
    };
    return {
      update
    };
  }
  function createTransitionStore() {
    let transitionStyle = null;
    const enable = (element) => {
      if (transitionStyle === null) {
        transitionStyle = element.style.transition;
      }
      element.style.transition = "bottom 0.3s ease-in-out";
    };
    const disable = (element) => {
      if (transitionStyle !== null) {
        element.style.transition = transitionStyle;
      }
    };
    return {
      enable,
      disable
    };
  }
  const menuStore = createMenuStore();
  const floatMenuStore = createFloatMenuStore();
  const transitionStore = createTransitionStore();
  // @license           MIT
  function moveToolbeltToTop(element, top) {
    const { height: toolbeltHeight } = element.getBoundingClientRect();
    const { innerHeight: viewportHeight } = window;
    const bottom = viewportHeight - toolbeltHeight - top;
    element.style.bottom = `${bottom}px`;
    return { left: null, bottom };
  }
  function moveToolbeltToBottom(element, bottom) {
    element.style.bottom = `${bottom}px`;
    return { left: null, bottom };
  }
  async function resizeWindow(element) {
    const { height } = element.getBoundingClientRect();
    const position = await getToolbeltPosition();
    if (position.bottom + height + DEFAULT_PADDING > window.innerHeight) {
      moveToolbeltToTop(element, DEFAULT_TOP);
    } else {
      moveToolbeltToBottom(element, position.bottom);
    }
  }
  async function registerInitialize(element) {
    transitionStore.enable(element);
    const data = await getToolbeltPosition();
    if (data.bottom !== null) {
      moveToolbeltToBottom(element, data.bottom);
      resizeWindow(element);
    }
    floatMenuStore.update(element);
  }
  function registerResizeWindow(element) {
    const onResize = () => {
      resizeWindow(element);
    };
    window.addEventListener("resize", onResize);
    return {
      unregister: () => {
        window.removeEventListener("resize", onResize);
      }
    };
  }
  function bindMouseEvent(element) {
    let isDragging = false;
    let startY = 0;
    let toolbeltY = 0;
    let position;
    const onMouseDown = (event) => {
      event.stopPropagation();
      startY = event.clientY;
      const { y } = element.getBoundingClientRect();
      toolbeltY = y;
      window.document.addEventListener("mousemove", onMouseMove);
      window.document.addEventListener("mouseup", onMouseUp);
      isDragging = true;
    };
    const onMouseMove = (event) => {
      if (!isDragging) {
        return;
      }
      const { clientY } = event;
      const offsetY = clientY - startY;
      position = moveToolbeltToTop(element, toolbeltY + offsetY);
    };
    const onMouseUp = (event) => {
      onMouseMove(event);
      isDragging = false;
      window.document.removeEventListener("mousemove", onMouseMove);
      window.document.removeEventListener("mouseup", onMouseUp);
    };
    return {
      enable: () => {
        element.addEventListener("mousedown", onMouseDown);
      },
      disable: () => {
        element.removeEventListener("mousedown", onMouseDown);
        return position;
      }
    };
  }
  function bindCursorStyle(element) {
    let cursorStyle = "default";
    const enable = () => {
      cursorStyle = element.style.cursor;
      element.style.cursor = "move";
    };
    const disable = () => {
      element.style.cursor = cursorStyle;
    };
    return {
      enable,
      disable
    };
  }
  // @license           MIT
  function registerDefaultMenu(element) {
    const onPin = () => {
      menuStore.update("DEFAULT", LABEL_UNPIN_DEFAULT_TOP, onUnpin);
      const position = moveToolbeltToTop(element, DEFAULT_TOP);
      setToolbeltPosition(position);
      floatMenuStore.update(element);
    };
    const onUnpin = () => {
      menuStore.update("DEFAULT", LABEL_PIN_DEFAULT_TOP, onPin);
      const position = moveToolbeltToBottom(element, DEFAULT_PADDING);
      setToolbeltPosition(position);
      floatMenuStore.update(element);
    };
    menuStore.update("DEFAULT", LABEL_PIN_DEFAULT_TOP, onPin);
  }
  function registerCustomizeMenu(element) {
    const cursorStyle = bindCursorStyle(element);
    const mouseEvent = bindMouseEvent(element);
    const onStart = () => {
      transitionStore.disable(element);
      menuStore.update("CUSTOMIZE", LABEL_STOP_CUSTOM_POSITION, onStop);
      cursorStyle.enable();
      mouseEvent.enable();
    };
    const onStop = () => {
      menuStore.update("CUSTOMIZE", LABEL_START_CUSTOM_POSITION, onStart);
      cursorStyle.disable();
      const position = mouseEvent.disable();
      setToolbeltPosition(position);
      floatMenuStore.update(element);
      transitionStore.enable(element);
    };
    menuStore.update("CUSTOMIZE", LABEL_START_CUSTOM_POSITION, onStart);
  }
  // @license           MIT
  (() => {
    function onReady() {
      const element = findToolbeltElement();
      if (!element) {
        console.error(
          `[${PREFIX}] Can not find figma toolbelt: \`${TOOLBELT_SELECTOR}\`.`
        );
        return;
      }
      registerInitialize(element);
      registerResizeWindow(element);
      registerDefaultMenu(element);
      registerCustomizeMenu(element);
      console.info(`[${PREFIX}] The script is enabled.`);
    }
    const _onload_ = window.onload || function() {
    };
    window.onload = (event) => {
      _onload_(event);
      setTimeout(onReady, 1e3);
    };
  })();
})();