(() => {
  var __create = Object.create;
  var __defProp = Object.defineProperty;
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  var __getOwnPropNames = Object.getOwnPropertyNames;
  var __getProtoOf = Object.getPrototypeOf;
  var __hasOwnProp = Object.prototype.hasOwnProperty;
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
    get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
  }) : x)(function(x) {
    if (typeof require !== "undefined") return require.apply(this, arguments);
    throw Error('Dynamic require of "' + x + '" is not supported');
  });
  var __commonJS = (cb, mod) => function __require2() {
    return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
  };
  var __copyProps = (to, from, except, desc) => {
    if (from && typeof from === "object" || typeof from === "function") {
      for (let key of __getOwnPropNames(from))
        if (!__hasOwnProp.call(to, key) && key !== except)
          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
    }
    return to;
  };
  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
    // If the importer is in node compatibility mode or this is not an ESM
    // file that has been converted to a CommonJS file using a Babel-
    // compatible transform (i.e. "__esModule" has not been set), then set
    // "default" to the CommonJS "module.exports" for node compatibility.
    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
    mod
  ));

  // src/shared/version.js
  var require_version = __commonJS({
    "src/shared/version.js"(exports, module) {
      (function attachBuildIdentity(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaBuildIdentity = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createBuildIdentity() {
        "use strict";
        const EXTENSION_VERSION = "1.25.34";
        const injectedFingerprint = true ? "ad6e691b8aca11ed33afdee80003f6267e631e774c668aa45122b2917e7c7890" : "";
        const fingerprint = injectedFingerprint || `source:${EXTENSION_VERSION}`;
        return Object.freeze({ version: EXTENSION_VERSION, fingerprint });
      });
    }
  });

  // src/shared/dock-logic.js
  var require_dock_logic = __commonJS({
    "src/shared/dock-logic.js"(exports, module) {
      (function exposeDockLogic(root, factory) {
        "use strict";
        const api = factory();
        if (typeof module === "object" && module.exports) {
          module.exports = api;
        }
        root.TfaDockLogic = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createDockLogic() {
        "use strict";
        const DOCK_STORAGE_KEY = "tfa_web_dock";
        const DOCK_TEXT_ICON_COLORS = Object.freeze([
          Object.freeze({ id: "violet", hex: "#6d5bd0" }),
          Object.freeze({ id: "blue", hex: "#2563eb" }),
          Object.freeze({ id: "cyan", hex: "#0e7490" }),
          Object.freeze({ id: "teal", hex: "#0f766e" }),
          Object.freeze({ id: "green", hex: "#15803d" }),
          Object.freeze({ id: "amber", hex: "#a16207" }),
          Object.freeze({ id: "orange", hex: "#c2410c" }),
          Object.freeze({ id: "red", hex: "#b91c1c" }),
          Object.freeze({ id: "rose", hex: "#be123c" }),
          Object.freeze({ id: "slate", hex: "#475569" })
        ]);
        const MOTION_LIMITS = Object.freeze({
          iconSize: Object.freeze([36, 72]),
          magnification: Object.freeze([1.2, 2.4]),
          influenceRadius: Object.freeze([80, 260]),
          response: Object.freeze([0.08, 0.32]),
          damping: Object.freeze([0.45, 0.92]),
          hideDelay: Object.freeze([0, 2e3])
        });
        const APPEARANCE_LIMITS = Object.freeze({
          tileSize: Object.freeze([44, 76]),
          trayPadding: Object.freeze([4, 24]),
          tileOpacity: Object.freeze([0, 100])
        });
        const FAVICON_SIZE_BUCKETS = Object.freeze([64, 128, 256, 512]);
        const DOCK_ICON_SOURCE_ORDER = Object.freeze({
          "page-svg": 0,
          "page-touch": 1,
          "page-icon": 2,
          saved: 3,
          tab: 4,
          chrome: 5
        });
        const DEFAULT_DOCK_CONFIG = Object.freeze({
          enabled: true,
          enabledUserSet: false,
          autoHide: false,
          sites: Object.freeze([]),
          motion: Object.freeze({
            iconSize: 48,
            magnification: 1.85,
            influenceRadius: 150,
            response: 0.18,
            damping: 0.72,
            hideDelay: 650
          }),
          appearance: Object.freeze({
            tileSize: 58,
            trayPadding: 10,
            tileColor: "#f8f8fa",
            tileOpacity: 100
          })
        });
        function clamp(value, limits) {
          return Math.min(limits[1], Math.max(limits[0], value));
        }
        function normalizeMotion(value) {
          const input = value && typeof value === "object" ? value : {};
          const motion = {};
          for (const key of Object.keys(MOTION_LIMITS)) {
            const candidate = input[key];
            motion[key] = Number.isFinite(candidate) ? clamp(candidate, MOTION_LIMITS[key]) : DEFAULT_DOCK_CONFIG.motion[key];
          }
          return motion;
        }
        function normalizeDockColor(value, fallback = DEFAULT_DOCK_CONFIG.appearance.tileColor) {
          const text = value == null ? "" : String(value).trim();
          if (/^#[\da-f]{6}$/i.test(text)) return text.toLowerCase();
          if (/^#[\da-f]{3}$/i.test(text)) {
            return `#${text.slice(1).split("").map((part) => part + part).join("")}`.toLowerCase();
          }
          return fallback;
        }
        function normalizeAppearance(value, legacyIconSize) {
          const input = value && typeof value === "object" ? value : null;
          const legacy = Number.isFinite(legacyIconSize) ? legacyIconSize : DEFAULT_DOCK_CONFIG.motion.iconSize;
          const migratedTile = clamp(
            Math.max(DEFAULT_DOCK_CONFIG.appearance.tileSize, legacy + 6),
            APPEARANCE_LIMITS.tileSize
          );
          return {
            tileSize: input && Number.isFinite(input.tileSize) ? clamp(input.tileSize, APPEARANCE_LIMITS.tileSize) : input ? DEFAULT_DOCK_CONFIG.appearance.tileSize : migratedTile,
            trayPadding: input && Number.isFinite(input.trayPadding) ? clamp(input.trayPadding, APPEARANCE_LIMITS.trayPadding) : DEFAULT_DOCK_CONFIG.appearance.trayPadding,
            tileColor: normalizeDockColor(
              input && input.tileColor,
              DEFAULT_DOCK_CONFIG.appearance.tileColor
            ),
            tileOpacity: input && Number.isFinite(input.tileOpacity) ? clamp(input.tileOpacity, APPEARANCE_LIMITS.tileOpacity) : DEFAULT_DOCK_CONFIG.appearance.tileOpacity
          };
        }
        function normalizeDockUrl(input) {
          let value = input == null ? "" : String(input).trim();
          if (!value) {
            throw new TypeError("\u8BF7\u8F93\u5165\u7F51\u9875\u5730\u5740");
          }
          const hasScheme = /^[a-z][a-z\d+.-]*:/i.test(value);
          if (!hasScheme) {
            value = `https://${value}`;
          }
          const parsed = new URL(value);
          if (!["http:", "https:", "file:"].includes(parsed.protocol)) {
            throw new TypeError("\u4E0D\u652F\u6301\u7684\u7F51\u5740\u534F\u8BAE");
          }
          return parsed.href;
        }
        function normalizeDockIconUrl(value) {
          const text = value == null ? "" : String(value).trim();
          if (!text || text.length > 8192) return "";
          try {
            const parsed = new URL(text);
            return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : "";
          } catch (_error) {
            return "";
          }
        }
        function normalizeDockIconText(value) {
          const text = value == null ? "" : String(value).trim();
          return /^[A-Za-z\u3400-\u4DBF\u4E00-\u9FFF]{1,10}$/.test(text) ? text : "";
        }
        function normalizeDockTextIconColor(value, fallback = "") {
          const text = value == null ? "" : String(value).trim().toLowerCase();
          const match = DOCK_TEXT_ICON_COLORS.find((color) => color.id === text || color.hex === text);
          if (match) return match.hex;
          const fallbackText = fallback == null ? "" : String(fallback).trim().toLowerCase();
          const fallbackMatch = DOCK_TEXT_ICON_COLORS.find((color) => color.id === fallbackText || color.hex === fallbackText);
          return fallbackMatch ? fallbackMatch.hex : "";
        }
        function safeEphemeralDockIconUrl(value, baseUrl) {
          const text = value == null ? "" : String(value).trim();
          if (!text || text.length > 65536) return "";
          if (/^data:image\/(?:png|gif|jpe?g|webp|svg\+xml)(?:;[^,]*)?,/i.test(text)) return text;
          try {
            const parsed = new URL(text, baseUrl);
            return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : "";
          } catch (_error) {
            return "";
          }
        }
        function compareDockIconCandidates(a, b) {
          const sourceDelta = (DOCK_ICON_SOURCE_ORDER[a && a.source] ?? 99) - (DOCK_ICON_SOURCE_ORDER[b && b.source] ?? 99);
          if (sourceDelta) return sourceDelta;
          const vectorDelta = Number(Boolean(b && b.vector)) - Number(Boolean(a && a.vector));
          if (vectorDelta) return vectorDelta;
          const sizeDelta = (Number(b && b.declaredSize) || 0) - (Number(a && a.declaredSize) || 0);
          if (sizeDelta) return sizeDelta;
          return String(a && a.url || "").localeCompare(String(b && b.url || ""));
        }
        function isFinalDockFaviconCandidate(candidate, naturalWidth, naturalHeight, requiredPixels) {
          if (!candidate || candidate.verified !== true) return false;
          if (candidate.vector === true) return true;
          const shortSide = Math.min(Number(naturalWidth) || 0, Number(naturalHeight) || 0);
          const threshold = Math.max(1, Math.ceil(Number(requiredPixels) || 1));
          return shortSide >= threshold;
        }
        function normalizeSite(site) {
          if (!site || typeof site !== "object") return null;
          const id = site.id == null ? "" : String(site.id).trim();
          if (!id) return null;
          try {
            const url = normalizeDockUrl(site.url);
            const suppliedTitle = site.title == null ? "" : String(site.title).trim();
            const title = suppliedTitle || new URL(url).hostname || url;
            const normalized = { id, title, url };
            const iconText = normalizeDockIconText(site.iconText);
            if (site.iconMode === "text" && iconText) {
              normalized.iconMode = "text";
              normalized.iconText = iconText;
              const iconColor = normalizeDockTextIconColor(site.iconColor);
              if (iconColor) normalized.iconColor = iconColor;
            } else {
              const iconUrl = normalizeDockIconUrl(site.iconUrl);
              if (iconUrl) {
                normalized.iconUrl = iconUrl;
                if (site.iconMode === "custom") normalized.iconMode = "custom";
              }
            }
            return normalized;
          } catch (_error) {
            return null;
          }
        }
        function normalizeDockConfig(value) {
          const input = value && typeof value === "object" ? value : {};
          const sites = [];
          const seenUrls = /* @__PURE__ */ new Set();
          const motion = normalizeMotion(input.motion);
          const appearance = normalizeAppearance(input.appearance, motion.iconSize);
          motion.iconSize = Math.min(motion.iconSize, appearance.tileSize - 6);
          if (Array.isArray(input.sites)) {
            for (const candidate of input.sites) {
              const site = normalizeSite(candidate);
              if (!site || seenUrls.has(site.url)) continue;
              seenUrls.add(site.url);
              sites.push(site);
            }
          }
          return {
            enabled: input.enabled !== false,
            enabledUserSet: input.enabledUserSet === true,
            autoHide: input.autoHide === true,
            sites,
            motion,
            appearance
          };
        }
        function isDockSiteFixed(config, url) {
          try {
            const normalizedUrl = normalizeDockUrl(url);
            return normalizeDockConfig(config).sites.some((site) => site.url === normalizedUrl);
          } catch (_error) {
            return false;
          }
        }
        function createSite(site) {
          const input = site && typeof site === "object" ? site : {};
          const url = normalizeDockUrl(input.url);
          const suppliedTitle = input.title == null ? "" : String(input.title).trim();
          const title = suppliedTitle || new URL(url).hostname || url;
          const suppliedId = input.id == null ? "" : String(input.id).trim();
          const randomSuffix = Math.random().toString(36).slice(2, 8) || "0";
          const id = suppliedId || `dock-${Date.now()}-${randomSuffix}`;
          const normalized = { id, title, url };
          const iconText = normalizeDockIconText(input.iconText);
          if (input.iconMode === "text" && iconText) {
            normalized.iconMode = "text";
            normalized.iconText = iconText;
            const iconColor = normalizeDockTextIconColor(input.iconColor);
            if (iconColor) normalized.iconColor = iconColor;
          } else {
            const iconUrl = normalizeDockIconUrl(input.iconUrl);
            if (iconUrl) {
              normalized.iconUrl = iconUrl;
              if (input.iconMode === "custom") normalized.iconMode = "custom";
            }
          }
          return normalized;
        }
        function upsertDockSite(config, site) {
          const normalized = normalizeDockConfig(config);
          const nextSite = createSite(site);
          const matchingIndex = normalized.sites.findIndex((candidate) => candidate.id === nextSite.id);
          const duplicate = normalized.sites.some((candidate, index) => candidate.url === nextSite.url && index !== matchingIndex);
          if (duplicate) {
            const error = new Error("\u8BE5\u7F51\u9875\u5DF2\u56FA\u5B9A");
            error.code = "DUPLICATE_URL";
            throw error;
          }
          if (matchingIndex === -1) {
            normalized.sites.push(nextSite);
          } else {
            normalized.sites[matchingIndex] = nextSite;
          }
          return normalized;
        }
        function removeDockSite(config, siteId) {
          const normalized = normalizeDockConfig(config);
          const id = siteId == null ? "" : String(siteId).trim();
          normalized.sites = normalized.sites.filter((site) => site.id !== id);
          return normalized;
        }
        function reorderDockSites(config, orderedIds) {
          const normalized = normalizeDockConfig(config);
          const remaining = normalized.sites.slice();
          const sites = [];
          if (Array.isArray(orderedIds)) {
            for (const rawId of orderedIds) {
              const id = rawId == null ? "" : String(rawId).trim();
              const index = remaining.findIndex((site) => site.id === id);
              if (index === -1) continue;
              sites.push(remaining.splice(index, 1)[0]);
            }
          }
          sites.push(...remaining);
          normalized.sites = sites;
          return normalized;
        }
        function moveDockSiteUrls(sites, fromIndex, insertionIndex) {
          const copy = Array.isArray(sites) ? sites.slice() : [];
          if (!Number.isInteger(fromIndex) || fromIndex < 0 || fromIndex >= copy.length) {
            return copy.map((site) => site.url);
          }
          const [moved] = copy.splice(fromIndex, 1);
          const target = Math.max(0, Math.min(copy.length, Number(insertionIndex) || 0));
          copy.splice(target, 0, moved);
          return copy.map((site) => site.url);
        }
        function reorderDockSitesByUrls(config, orderedUrls) {
          const normalized = normalizeDockConfig(config);
          const remaining = normalized.sites.slice();
          const sites = [];
          for (const rawUrl of Array.isArray(orderedUrls) ? orderedUrls : []) {
            let url;
            try {
              url = normalizeDockUrl(rawUrl);
            } catch (_error) {
              continue;
            }
            const index = remaining.findIndex((site) => site.url === url);
            if (index !== -1) sites.push(remaining.splice(index, 1)[0]);
          }
          normalized.sites = sites.concat(remaining);
          return normalized;
        }
        function toggleDockSite(config, site) {
          const normalized = normalizeDockConfig(config);
          const url = normalizeDockUrl(site && site.url);
          const fixedIndex = normalized.sites.findIndex((candidate) => candidate.url === url);
          if (fixedIndex !== -1) {
            normalized.sites.splice(fixedIndex, 1);
            return { config: normalized, fixed: false };
          }
          return { config: upsertDockSite(normalized, site), fixed: true };
        }
        function computeDockTargets(centers, pointerX, motion, appearance) {
          const positions = Array.isArray(centers) ? centers : [];
          if (!Number.isFinite(pointerX)) {
            return positions.map(() => ({ scale: 1, x: 0, y: 0 }));
          }
          const safeMotion = normalizeMotion(motion);
          const safeAppearance = normalizeAppearance(appearance, safeMotion.iconSize);
          const scales = positions.map((center) => {
            if (!Number.isFinite(center)) return 1;
            const distance = Math.abs(center - pointerX);
            if (distance >= safeMotion.influenceRadius) return 1;
            const phase = distance / safeMotion.influenceRadius * Math.PI / 2;
            const influence = Math.cos(phase) ** 2;
            return 1 + (safeMotion.magnification - 1) * influence;
          });
          const extraWidths = scales.map((scale) => (scale - 1) * safeAppearance.tileSize);
          return scales.map((scale, index) => {
            let leftExtra = 0;
            let rightExtra = 0;
            for (let other = 0; other < extraWidths.length; other += 1) {
              if (other < index) leftExtra += extraWidths[other];
              if (other > index) rightExtra += extraWidths[other];
            }
            return {
              scale,
              x: (leftExtra - rightExtra) / 2,
              y: -(scale - 1) * safeAppearance.tileSize * 0.58
            };
          });
        }
        function computeDockTooltipPlacement({
          viewportWidth,
          itemCenter,
          tooltipWidth,
          gutter = 8
        } = {}) {
          const safeViewportWidth = Number.isFinite(viewportWidth) && viewportWidth > 0 ? viewportWidth : 0;
          const requestedGutter = Number.isFinite(gutter) && gutter > 0 ? gutter : 0;
          const safeGutter = Math.min(requestedGutter, safeViewportWidth / 2);
          const maxTooltipWidth = Math.max(0, safeViewportWidth - safeGutter * 2);
          const safeTooltipWidth = Math.min(
            Number.isFinite(tooltipWidth) && tooltipWidth > 0 ? tooltipWidth : 0,
            maxTooltipWidth
          );
          const halfWidth = safeTooltipWidth / 2;
          const safeItemCenter = Number.isFinite(itemCenter) ? itemCenter : safeViewportWidth / 2;
          const minCenter = safeGutter + halfWidth;
          const maxCenter = safeViewportWidth - safeGutter - halfWidth;
          const bubbleCenter = minCenter <= maxCenter ? clamp(safeItemCenter, [minCenter, maxCenter]) : safeViewportWidth / 2;
          const arrowRange = Math.max(0, halfWidth - 11);
          const arrowShift = clamp(safeItemCenter - bubbleCenter, [-arrowRange, arrowRange]);
          return {
            bubbleCenter,
            labelShift: bubbleCenter - safeItemCenter,
            arrowShift
          };
        }
        function projectDockPointerX(baseCenter, pointerX, visualRect, iconSize) {
          if (!Number.isFinite(pointerX)) return pointerX;
          if (!Number.isFinite(baseCenter) || !visualRect || !Number.isFinite(visualRect.left) || !Number.isFinite(visualRect.width) || visualRect.width <= 0) {
            return pointerX;
          }
          const safeIconSize = Number.isFinite(iconSize) && iconSize > 0 ? iconSize : DEFAULT_DOCK_CONFIG.motion.iconSize;
          const visualLeft = visualRect.left;
          const visualRight = visualLeft + visualRect.width;
          const clampedPointer = clamp(pointerX, [visualLeft, visualRight]);
          const visualCenter = visualLeft + visualRect.width / 2;
          return baseCenter + (clampedPointer - visualCenter) * (safeIconSize / visualRect.width);
        }
        function requiredDockFaviconPixels(motion, devicePixelRatio) {
          const safeMotion = normalizeMotion(motion);
          const dpr = Number.isFinite(devicePixelRatio) && devicePixelRatio > 0 ? Math.max(1, devicePixelRatio) : 2;
          return Math.ceil(safeMotion.iconSize * safeMotion.magnification * dpr);
        }
        function selectDockFaviconSize(motion, devicePixelRatio) {
          const required = requiredDockFaviconPixels(motion, devicePixelRatio);
          return FAVICON_SIZE_BUCKETS.find((size) => size >= required) || 512;
        }
        function stepDockSpring(state, target, dt, motion) {
          const input = state && typeof state === "object" ? state : {};
          const value = Number.isFinite(input.value) ? input.value : 0;
          const oldVelocity = Number.isFinite(input.velocity) ? input.velocity : 0;
          const targetValue = Number.isFinite(target) ? target : value;
          const frameInput = Number.isFinite(dt) ? dt * 60 : 1;
          const frame = clamp(frameInput, [0.25, 2]);
          const safeMotion = normalizeMotion(motion);
          const acceleration = (targetValue - value) * safeMotion.response * frame;
          const velocity = (oldVelocity + acceleration) * safeMotion.damping ** frame;
          return { value: value + velocity, velocity };
        }
        return {
          DOCK_STORAGE_KEY,
          DOCK_TEXT_ICON_COLORS,
          MOTION_LIMITS,
          APPEARANCE_LIMITS,
          FAVICON_SIZE_BUCKETS,
          DEFAULT_DOCK_CONFIG,
          normalizeDockUrl,
          normalizeDockIconUrl,
          normalizeDockIconText,
          normalizeDockTextIconColor,
          normalizeDockColor,
          safeEphemeralDockIconUrl,
          compareDockIconCandidates,
          isFinalDockFaviconCandidate,
          normalizeAppearance,
          normalizeDockConfig,
          isDockSiteFixed,
          upsertDockSite,
          removeDockSite,
          reorderDockSites,
          reorderDockSitesByUrls,
          moveDockSiteUrls,
          toggleDockSite,
          computeDockTargets,
          computeDockTooltipPlacement,
          projectDockPointerX,
          requiredDockFaviconPixels,
          selectDockFaviconSize,
          stepDockSpring
        };
      });
    }
  });

  // src/shared/dock-search-contract.js
  var require_dock_search_contract = __commonJS({
    "src/shared/dock-search-contract.js"(exports, module) {
      (function attachDockSearchContract(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        if (typeof root === "object") root.TfaDockSearchContract = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createDockSearchContract() {
        "use strict";
        const SOURCE_ORDER = Object.freeze(["common", "fixed", "tab", "bookmark", "browser", "web", "unknown"]);
        const SOURCE_ALIASES = Object.freeze({
          favorite: "common",
          frequent: "common",
          pinned: "fixed",
          history: "browser"
        });
        function normalizeQuery(value) {
          return String(value == null ? "" : value).trim().replace(/\s+/g, " ").slice(0, 500);
        }
        function normalizeSource(value) {
          const source = String(value || "").trim().toLowerCase();
          const normalized = SOURCE_ALIASES[source] || source;
          return SOURCE_ORDER.includes(normalized) ? normalized : "unknown";
        }
        function normalizePermission(status, fallback = null) {
          if (!status || typeof status !== "object") return fallback;
          return Object.freeze({
            granted: status.granted === true,
            code: String(status.code || ""),
            message: String(status.message || ""),
            recoverable: status.recoverable !== false
          });
        }
        function mergePermission(current, incoming) {
          const next = normalizePermission(incoming, current);
          if (!next) return current;
          if (next.granted) return next;
          if (String(current?.code || "").endsWith("_DENIED") && String(next.code || "").endsWith("_REQUIRED")) return current;
          return next;
        }
        function permissionState(permissions = {}) {
          return Object.freeze({
            bookmarks: normalizePermission(permissions.bookmarks),
            history: normalizePermission(permissions.history)
          });
        }
        function resultState(query, suggestions, permissions = {}) {
          if (!normalizeQuery(query)) return "idle";
          const statuses = [permissions.bookmarks, permissions.history].filter(Boolean);
          if (statuses.some((status) => String(status.code || "").endsWith("_DENIED"))) return "denied";
          if (statuses.some((status) => status.granted === false)) return "required";
          const fullyGranted = statuses.length === 2 && statuses.every((status) => status.granted === true);
          const hasInternalResult = (Array.isArray(suggestions) ? suggestions : []).some((suggestion) => normalizeSource(suggestion?.source) !== "web");
          if (fullyGranted && hasInternalResult) return "granted-with-results";
          if (fullyGranted) return "fully-granted-no-match";
          return "unknown";
        }
        function normalizedUrl(value) {
          try {
            const url = new URL(String(value || ""));
            if (!["http:", "https:"].includes(url.protocol)) return "";
            url.hash = "";
            return url.href;
          } catch (_) {
            return "";
          }
        }
        function normalizeSuggestion(item) {
          const url = normalizedUrl(item?.url);
          if (!url) return null;
          const source = normalizeSource(item?.source);
          return Object.freeze({
            ...item,
            source,
            title: String(item?.title || new URL(url).hostname || url).trim(),
            url,
            ...source === "tab" && Number.isInteger(item?.tabId) ? { tabId: item.tabId } : {}
          });
        }
        function composeSuggestions(...collections) {
          const buckets = Object.fromEntries(SOURCE_ORDER.map((source) => [source, []]));
          const seen = /* @__PURE__ */ new Set();
          collections.flatMap((items) => Array.isArray(items) ? items : []).forEach((item) => {
            const suggestion = normalizeSuggestion(item);
            if (!suggestion || seen.has(suggestion.url)) return;
            seen.add(suggestion.url);
            buckets[suggestion.source].push(suggestion);
          });
          return Object.freeze(SOURCE_ORDER.flatMap((source) => buckets[source]));
        }
        function searchLocalSites({
          query,
          sites = [],
          catalogSites = [],
          pinnedUrls = [],
          limit = 7
        } = {}) {
          const needle = normalizeQuery(query).toLocaleLowerCase();
          if (!needle) return Object.freeze([]);
          const pinned = pinnedUrls instanceof Set ? pinnedUrls : new Set(pinnedUrls);
          const records = [
            ...Array.isArray(sites) ? sites : [],
            ...(Array.isArray(catalogSites) ? catalogSites : []).map((site) => ({
              id: site.id,
              title: site.title,
              url: site.url,
              iconKind: "catalog",
              iconValue: site.iconFile,
              iconColor: "violet"
            }))
          ].filter((site) => `${site?.title || ""} ${site?.url || ""}`.toLocaleLowerCase().includes(needle)).map((site) => ({ ...site, source: pinned.has(site.url) ? "fixed" : "common" }));
          const maximum = Number.isFinite(Number(limit)) ? Math.max(1, Math.trunc(Number(limit))) : 7;
          const composed = composeSuggestions(records);
          const common = composed.filter((item) => item.source === "common");
          const fixed = composed.filter((item) => item.source === "fixed");
          if (!common.length || !fixed.length) return Object.freeze(composed.slice(0, maximum));
          const commonQuota = Math.ceil(maximum / 2);
          const fixedQuota = Math.floor(maximum / 2);
          const selected = [...common.slice(0, commonQuota), ...fixed.slice(0, fixedQuota)];
          if (selected.length < maximum) {
            const chosen = new Set(selected.map((item) => item.url));
            selected.push(...composed.filter((item) => !chosen.has(item.url)).slice(0, maximum - selected.length));
          }
          return Object.freeze([...composeSuggestions(selected)]);
        }
        return Object.freeze({
          SOURCE_ORDER,
          normalizeQuery,
          normalizeSource,
          normalizePermission,
          mergePermission,
          permissionState,
          resultState,
          composeSuggestions,
          searchLocalSites
        });
      });
    }
  });

  // src/shared/web-dock-catalog.js
  var require_web_dock_catalog = __commonJS({
    "src/shared/web-dock-catalog.js"(exports, module) {
      (() => {
        const rows = [
          ["chatgpt", "ChatGPT", "https://chatgpt.com/", "AI"],
          ["claude", "Claude", "https://claude.ai/", "AI"],
          ["gemini", "Gemini", "https://gemini.google.com/", "AI"],
          ["copilot", "Microsoft Copilot", "https://copilot.microsoft.com/", "AI"],
          ["perplexity", "Perplexity", "https://www.perplexity.ai/", "AI"],
          ["grok", "Grok", "https://grok.com/", "AI"],
          ["deepseek", "DeepSeek", "https://chat.deepseek.com/", "AI"],
          ["qwen", "\u901A\u4E49\u5343\u95EE", "https://www.qianwen.com/", "AI"],
          ["kimi", "Kimi", "https://www.kimi.com/", "AI"],
          ["doubao", "\u8C46\u5305", "https://www.doubao.com/", "AI"],
          ["wenxin", "\u6587\u5FC3\u4E00\u8A00", "https://yiyan.baidu.com/", "AI"],
          ["yuanbao", "\u817E\u8BAF\u5143\u5B9D", "https://yuanbao.tencent.com/", "AI"],
          ["zhipu", "\u667A\u8C31\u6E05\u8A00", "https://chatglm.cn/", "AI"],
          ["mistral", "Mistral AI", "https://chat.mistral.ai/", "AI"],
          ["huggingface", "Hugging Face", "https://huggingface.co/", "AI"],
          ["poe", "Poe", "https://poe.com/", "AI"],
          ["cohere", "Cohere", "https://cohere.com/", "AI"],
          ["characterai", "Character.AI", "https://character.ai/", "AI"],
          ["midjourney", "Midjourney", "https://www.midjourney.com/", "AI"],
          ["suno", "Suno", "https://suno.com/", "AI"],
          ["google", "Google", "https://www.google.com/", "\u5DE5\u5177"],
          ["bing", "Bing", "https://www.bing.com/", "\u5DE5\u5177"],
          ["baidu", "\u767E\u5EA6", "https://www.baidu.com/", "\u5DE5\u5177"],
          ["yahoo", "Yahoo", "https://www.yahoo.com/", "\u5DE5\u5177"],
          ["duckduckgo", "DuckDuckGo", "https://duckduckgo.com/", "\u5DE5\u5177"],
          ["yandex", "Yandex", "https://yandex.com/", "\u5DE5\u5177"],
          ["naver", "Naver", "https://www.naver.com/", "\u5DE5\u5177"],
          ["notion", "Notion", "https://www.notion.so/", "\u5DE5\u5177"],
          ["googledrive", "Google Drive", "https://drive.google.com/", "\u5DE5\u5177"],
          ["gmail", "Gmail", "https://mail.google.com/", "\u5DE5\u5177"],
          ["outlook", "Outlook", "https://outlook.live.com/", "\u5DE5\u5177"],
          ["dropbox", "Dropbox", "https://www.dropbox.com/", "\u5DE5\u5177"],
          ["onedrive", "OneDrive", "https://onedrive.live.com/", "\u5DE5\u5177"],
          ["box", "Box", "https://www.box.com/", "\u5DE5\u5177"],
          ["slack", "Slack", "https://slack.com/", "\u5DE5\u5177"],
          ["trello", "Trello", "https://trello.com/", "\u5DE5\u5177"],
          ["asana", "Asana", "https://asana.com/", "\u5DE5\u5177"],
          ["airtable", "Airtable", "https://www.airtable.com/", "\u5DE5\u5177"],
          ["evernote", "Evernote", "https://evernote.com/", "\u5DE5\u5177"],
          ["zoom", "Zoom", "https://zoom.us/", "\u5DE5\u5177"],
          ["facebook", "Facebook", "https://www.facebook.com/", "\u793E\u4EA4"],
          ["instagram", "Instagram", "https://www.instagram.com/", "\u793E\u4EA4"],
          ["x", "X", "https://x.com/", "\u793E\u4EA4"],
          ["threads", "Threads", "https://www.threads.net/", "\u793E\u4EA4"],
          ["tiktok", "TikTok", "https://www.tiktok.com/", "\u793E\u4EA4"],
          ["linkedin", "LinkedIn", "https://www.linkedin.com/", "\u793E\u4EA4"],
          ["reddit", "Reddit", "https://www.reddit.com/", "\u793E\u4EA4"],
          ["pinterest", "Pinterest", "https://www.pinterest.com/", "\u793E\u4EA4"],
          ["discord", "Discord", "https://discord.com/", "\u793E\u4EA4"],
          ["telegram", "Telegram", "https://telegram.org/", "\u793E\u4EA4"],
          ["whatsapp", "WhatsApp", "https://www.whatsapp.com/", "\u793E\u4EA4"],
          ["wechat", "\u5FAE\u4FE1", "https://weixin.qq.com/", "\u793E\u4EA4"],
          ["qq", "QQ", "https://im.qq.com/", "\u793E\u4EA4"],
          ["weibo", "\u5FAE\u535A", "https://weibo.com/", "\u793E\u4EA4"],
          ["zhihu", "\u77E5\u4E4E", "https://www.zhihu.com/", "\u793E\u4EA4"],
          ["douban", "\u8C46\u74E3", "https://www.douban.com/", "\u793E\u4EA4"],
          ["xiaohongshu", "\u5C0F\u7EA2\u4E66", "https://www.xiaohongshu.com/", "\u793E\u4EA4"],
          ["kuaishou", "\u5FEB\u624B", "https://www.kuaishou.com/", "\u793E\u4EA4"],
          ["bilibili", "\u54D4\u54E9\u54D4\u54E9", "https://www.bilibili.com/", "\u793E\u4EA4"],
          ["douyin", "\u6296\u97F3", "https://www.douyin.com/", "\u793E\u4EA4"],
          ["youtube", "YouTube", "https://www.youtube.com/", "\u5F71\u97F3"],
          ["netflix", "Netflix", "https://www.netflix.com/", "\u5F71\u97F3"],
          ["spotify", "Spotify", "https://open.spotify.com/", "\u5F71\u97F3"],
          ["applemusic", "Apple Music", "https://music.apple.com/", "\u5F71\u97F3"],
          ["soundcloud", "SoundCloud", "https://soundcloud.com/", "\u5F71\u97F3"],
          ["twitch", "Twitch", "https://www.twitch.tv/", "\u5F71\u97F3"],
          ["vimeo", "Vimeo", "https://vimeo.com/", "\u5F71\u97F3"],
          ["disneyplus", "Disney+", "https://www.disneyplus.com/", "\u5F71\u97F3"],
          ["primevideo", "Prime Video", "https://www.primevideo.com/", "\u5F71\u97F3"],
          ["max", "Max", "https://www.max.com/", "\u5F71\u97F3"],
          ["hulu", "Hulu", "https://www.hulu.com/", "\u5F71\u97F3"],
          ["iqiyi", "\u7231\u5947\u827A", "https://www.iqiyi.com/", "\u5F71\u97F3"],
          ["tencentvideo", "\u817E\u8BAF\u89C6\u9891", "https://v.qq.com/", "\u5F71\u97F3"],
          ["youku", "\u4F18\u9177", "https://www.youku.com/", "\u5F71\u97F3"],
          ["mangotv", "\u8292\u679CTV", "https://www.mgtv.com/", "\u5F71\u97F3"],
          ["neteasemusic", "\u7F51\u6613\u4E91\u97F3\u4E50", "https://music.163.com/", "\u5F71\u97F3"],
          ["qqmusic", "QQ\u97F3\u4E50", "https://y.qq.com/", "\u5F71\u97F3"],
          ["kugou", "\u9177\u72D7\u97F3\u4E50", "https://www.kugou.com/", "\u5F71\u97F3"],
          ["ximalaya", "\u559C\u9A6C\u62C9\u96C5", "https://www.ximalaya.com/", "\u5F71\u97F3"],
          ["bandcamp", "Bandcamp", "https://bandcamp.com/", "\u5F71\u97F3"],
          ["github", "GitHub", "https://github.com/", "\u5F00\u53D1"],
          ["gitlab", "GitLab", "https://gitlab.com/", "\u5F00\u53D1"],
          ["bitbucket", "Bitbucket", "https://bitbucket.org/", "\u5F00\u53D1"],
          ["stackoverflow", "Stack Overflow", "https://stackoverflow.com/", "\u5F00\u53D1"],
          ["npm", "npm", "https://www.npmjs.com/", "\u5F00\u53D1"],
          ["mdn", "MDN", "https://developer.mozilla.org/", "\u5F00\u53D1"],
          ["docker", "Docker", "https://www.docker.com/", "\u5F00\u53D1"],
          ["kubernetes", "Kubernetes", "https://kubernetes.io/", "\u5F00\u53D1"],
          ["vercel", "Vercel", "https://vercel.com/", "\u5F00\u53D1"],
          ["netlify", "Netlify", "https://www.netlify.com/", "\u5F00\u53D1"],
          ["cloudflare", "Cloudflare", "https://www.cloudflare.com/", "\u5F00\u53D1"],
          ["aws", "AWS", "https://aws.amazon.com/", "\u5F00\u53D1"],
          ["azure", "Microsoft Azure", "https://azure.microsoft.com/", "\u5F00\u53D1"],
          ["googlecloud", "Google Cloud", "https://cloud.google.com/", "\u5F00\u53D1"],
          ["firebase", "Firebase", "https://firebase.google.com/", "\u5F00\u53D1"],
          ["supabase", "Supabase", "https://supabase.com/", "\u5F00\u53D1"],
          ["codepen", "CodePen", "https://codepen.io/", "\u5F00\u53D1"],
          ["replit", "Replit", "https://replit.com/", "\u5F00\u53D1"],
          ["leetcode", "LeetCode", "https://leetcode.com/", "\u5F00\u53D1"],
          ["hackerrank", "HackerRank", "https://www.hackerrank.com/", "\u5F00\u53D1"],
          ["jetbrains", "JetBrains", "https://www.jetbrains.com/", "\u5F00\u53D1"],
          ["vscode", "Visual Studio Code", "https://code.visualstudio.com/", "\u5F00\u53D1"],
          ["pypi", "PyPI", "https://pypi.org/", "\u5F00\u53D1"],
          ["rust", "Rust", "https://www.rust-lang.org/", "\u5F00\u53D1"],
          ["golang", "Go", "https://go.dev/", "\u5F00\u53D1"],
          ["figma", "Figma", "https://www.figma.com/", "\u8BBE\u8BA1"],
          ["canva", "Canva", "https://www.canva.com/", "\u8BBE\u8BA1"],
          ["dribbble", "Dribbble", "https://dribbble.com/", "\u8BBE\u8BA1"],
          ["behance", "Behance", "https://www.behance.net/", "\u8BBE\u8BA1"],
          ["adobe", "Adobe", "https://www.adobe.com/", "\u8BBE\u8BA1"],
          ["photoshop", "Photoshop Web", "https://photoshop.adobe.com/", "\u8BBE\u8BA1"],
          ["pixiv", "Pixiv", "https://www.pixiv.net/", "\u8BBE\u8BA1"],
          ["pixabay", "Pixabay", "https://pixabay.com/", "\u8BBE\u8BA1"],
          ["unsplash", "Unsplash", "https://unsplash.com/", "\u8BBE\u8BA1"],
          ["pexels", "Pexels", "https://www.pexels.com/", "\u8BBE\u8BA1"],
          ["freepik", "Freepik", "https://www.freepik.com/", "\u8BBE\u8BA1"],
          ["sketch", "Sketch", "https://www.sketch.com/", "\u8BBE\u8BA1"],
          ["framer", "Framer", "https://www.framer.com/", "\u8BBE\u8BA1"],
          ["miro", "Miro", "https://miro.com/", "\u8BBE\u8BA1"],
          ["excalidraw", "Excalidraw", "https://excalidraw.com/", "\u8BBE\u8BA1"],
          ["amazon", "Amazon", "https://www.amazon.com/", "\u8D2D\u7269"],
          ["ebay", "eBay", "https://www.ebay.com/", "\u8D2D\u7269"],
          ["aliexpress", "AliExpress", "https://www.aliexpress.com/", "\u8D2D\u7269"],
          ["walmart", "Walmart", "https://www.walmart.com/", "\u8D2D\u7269"],
          ["etsy", "Etsy", "https://www.etsy.com/", "\u8D2D\u7269"],
          ["shopify", "Shopify", "https://www.shopify.com/", "\u8D2D\u7269"],
          ["temu", "Temu", "https://www.temu.com/", "\u8D2D\u7269"],
          ["taobao", "\u6DD8\u5B9D", "https://www.taobao.com/", "\u8D2D\u7269"],
          ["tmall", "\u5929\u732B", "https://www.tmall.com/", "\u8D2D\u7269"],
          ["jd", "\u4EAC\u4E1C", "https://www.jd.com/", "\u8D2D\u7269"],
          ["pinduoduo", "\u62FC\u591A\u591A", "https://www.pinduoduo.com/", "\u8D2D\u7269"],
          ["suning", "\u82CF\u5B81\u6613\u8D2D", "https://www.suning.com/", "\u8D2D\u7269"],
          ["vipshop", "\u552F\u54C1\u4F1A", "https://www.vip.com/", "\u8D2D\u7269"],
          ["dangdang", "\u5F53\u5F53", "https://www.dangdang.com/", "\u8D2D\u7269"],
          ["xianyu", "\u95F2\u9C7C", "https://www.goofish.com/", "\u8D2D\u7269"],
          ["meituan", "\u7F8E\u56E2", "https://www.meituan.com/", "\u8D2D\u7269"],
          ["dianping", "\u5927\u4F17\u70B9\u8BC4", "https://www.dianping.com/", "\u8D2D\u7269"],
          ["ctrip", "\u643A\u7A0B", "https://www.ctrip.com/", "\u8D2D\u7269"],
          ["qunar", "\u53BB\u54EA\u513F", "https://www.qunar.com/", "\u8D2D\u7269"],
          ["airbnb", "Airbnb", "https://www.airbnb.com/", "\u8D2D\u7269"],
          ["360", "360\u641C\u7D22", "https://www.so.com/", "\u56FD\u5185"],
          ["sogou", "\u641C\u72D7", "https://www.sogou.com/", "\u56FD\u5185"],
          ["shenma", "\u795E\u9A6C\u641C\u7D22", "https://m.sm.cn/", "\u56FD\u5185"],
          ["netease", "\u7F51\u6613", "https://www.163.com/", "\u56FD\u5185"],
          ["sina", "\u65B0\u6D6A", "https://www.sina.com.cn/", "\u56FD\u5185"],
          ["sohu", "\u641C\u72D0", "https://www.sohu.com/", "\u56FD\u5185"],
          ["toutiao", "\u4ECA\u65E5\u5934\u6761", "https://www.toutiao.com/", "\u56FD\u5185"],
          ["thepaper", "\u6F8E\u6E43\u65B0\u95FB", "https://www.thepaper.cn/", "\u56FD\u5185"],
          ["caixin", "\u8D22\u65B0", "https://www.caixin.com/", "\u56FD\u5185"],
          ["huxiu", "\u864E\u55C5", "https://www.huxiu.com/", "\u56FD\u5185"],
          ["36kr", "36\u6C2A", "https://36kr.com/", "\u56FD\u5185"],
          ["eastmoney", "\u4E1C\u65B9\u8D22\u5BCC", "https://www.eastmoney.com/", "\u56FD\u5185"],
          ["xueqiu", "\u96EA\u7403", "https://xueqiu.com/", "\u56FD\u5185"],
          ["10jqka", "\u540C\u82B1\u987A", "https://www.10jqka.com.cn/", "\u56FD\u5185"],
          ["boss", "BOSS\u76F4\u8058", "https://www.zhipin.com/", "\u56FD\u5185"],
          ["zhaopin", "\u667A\u8054\u62DB\u8058", "https://www.zhaopin.com/", "\u56FD\u5185"],
          ["51job", "\u524D\u7A0B\u65E0\u5FE7", "https://www.51job.com/", "\u56FD\u5185"],
          ["liepin", "\u730E\u8058", "https://www.liepin.com/", "\u56FD\u5185"],
          ["dingtalk", "\u9489\u9489", "https://www.dingtalk.com/", "\u56FD\u5185"],
          ["feishu", "\u98DE\u4E66", "https://www.feishu.cn/", "\u56FD\u5185"],
          ["tencentdocs", "\u817E\u8BAF\u6587\u6863", "https://docs.qq.com/", "\u56FD\u5185"],
          ["wps", "WPS", "https://www.wps.cn/", "\u56FD\u5185"],
          ["youdao", "\u6709\u9053", "https://www.youdao.com/", "\u56FD\u5185"],
          ["baidupan", "\u767E\u5EA6\u7F51\u76D8", "https://pan.baidu.com/", "\u56FD\u5185"],
          ["aliyun", "\u963F\u91CC\u4E91", "https://www.aliyun.com/", "\u56FD\u5185"],
          ["tencentcloud", "\u817E\u8BAF\u4E91", "https://cloud.tencent.com/", "\u56FD\u5185"],
          ["huaweicloud", "\u534E\u4E3A\u4E91", "https://www.huaweicloud.com/", "\u56FD\u5185"],
          ["gitee", "Gitee", "https://gitee.com/", "\u56FD\u5185"],
          ["csdn", "CSDN", "https://www.csdn.net/", "\u56FD\u5185"],
          ["juejin", "\u7A00\u571F\u6398\u91D1", "https://juejin.cn/", "\u56FD\u5185"],
          ["cnblogs", "\u535A\u5BA2\u56ED", "https://www.cnblogs.com/", "\u56FD\u5185"],
          ["segmentfault", "SegmentFault", "https://segmentfault.com/", "\u56FD\u5185"],
          ["oschina", "\u5F00\u6E90\u4E2D\u56FD", "https://www.oschina.net/", "\u56FD\u5185"],
          ["lanzou", "\u84DD\u594F\u4E91", "https://www.lanzou.com/", "\u56FD\u5185"],
          ["quark", "\u5938\u514B", "https://www.quark.cn/", "\u56FD\u5185"],
          ["uc", "UC\u6D4F\u89C8\u5668", "https://www.uc.cn/", "\u56FD\u5185"],
          ["amap", "\u9AD8\u5FB7\u5730\u56FE", "https://www.amap.com/", "\u56FD\u5185"],
          ["baidumaps", "\u767E\u5EA6\u5730\u56FE", "https://map.baidu.com/", "\u56FD\u5185"],
          ["eleme", "\u997F\u4E86\u4E48", "https://www.ele.me/", "\u56FD\u5185"],
          ["cainiao", "\u83DC\u9E1F", "https://www.cainiao.com/", "\u56FD\u5185"],
          ["wikipedia", "Wikipedia", "https://www.wikipedia.org/", "\u77E5\u8BC6"],
          ["britannica", "Britannica", "https://www.britannica.com/", "\u77E5\u8BC6"],
          ["medium", "Medium", "https://medium.com/", "\u77E5\u8BC6"],
          ["quora", "Quora", "https://www.quora.com/", "\u77E5\u8BC6"],
          ["substack", "Substack", "https://substack.com/", "\u77E5\u8BC6"],
          ["nytimes", "The New York Times", "https://www.nytimes.com/", "\u77E5\u8BC6"],
          ["bbc", "BBC", "https://www.bbc.com/", "\u77E5\u8BC6"],
          ["cnn", "CNN", "https://www.cnn.com/", "\u77E5\u8BC6"],
          ["reuters", "Reuters", "https://www.reuters.com/", "\u77E5\u8BC6"],
          ["bloomberg", "Bloomberg", "https://www.bloomberg.com/", "\u77E5\u8BC6"],
          ["guardian", "The Guardian", "https://www.theguardian.com/", "\u77E5\u8BC6"],
          ["wsj", "The Wall Street Journal", "https://www.wsj.com/", "\u77E5\u8BC6"],
          ["ft", "Financial Times", "https://www.ft.com/", "\u77E5\u8BC6"],
          ["npr", "NPR", "https://www.npr.org/", "\u77E5\u8BC6"],
          ["apnews", "AP News", "https://apnews.com/", "\u77E5\u8BC6"],
          ["economist", "The Economist", "https://www.economist.com/", "\u77E5\u8BC6"],
          ["gitbook", "GitBook", "https://www.gitbook.com/", "\u77E5\u8BC6"],
          ["archive", "Internet Archive", "https://archive.org/", "\u77E5\u8BC6"],
          ["coursera", "Coursera", "https://www.coursera.org/", "\u77E5\u8BC6"],
          ["edx", "edX", "https://www.edx.org/", "\u77E5\u8BC6"]
        ];
        const svgIds = /* @__PURE__ */ new Set([
          "airbnb",
          "applemusic",
          "baidu",
          "bandcamp",
          "bbc",
          "copilot",
          "github",
          "golang",
          "google",
          "juejin",
          "lanzou",
          "rust",
          "shenma",
          "threads",
          "vimeo",
          "whatsapp",
          "x"
        ]);
        const sites = rows.map(([id, title, url, category]) => Object.freeze({
          id,
          title,
          url,
          category,
          iconFile: `web-dock-icons/${id}.${svgIds.has(id) ? "svg" : "png"}`
        }));
        const byHost = /* @__PURE__ */ new Map();
        for (const site of sites) {
          try {
            const host = new URL(site.url).hostname.replace(/^www\./, "");
            byHost.set(host, site);
          } catch (_) {
          }
        }
        const api = Object.freeze({
          sites: Object.freeze(sites),
          findByUrl(value) {
            try {
              const host = new URL(value).hostname.replace(/^www\./, "");
              return byHost.get(host) || null;
            } catch (_) {
              return null;
            }
          }
        });
        globalThis.TfaWebDockCatalog = api;
        if (typeof module === "object" && module.exports) module.exports = api;
      })();
    }
  });

  // src/shared/web-dock-recently-closed-logic.js
  var require_web_dock_recently_closed_logic = __commonJS({
    "src/shared/web-dock-recently-closed-logic.js"(exports, module) {
      (function attachWebDockRecentlyClosedLogic(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaWebDockRecentlyClosedLogic = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createModule() {
        "use strict";
        const MAX_ITEMS = 25;
        function webUrl(value) {
          try {
            const parsed = new URL(String(value || "").trim());
            return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed.href : "";
          } catch (_error) {
            return "";
          }
        }
        function faviconUrl(value) {
          const raw = String(value || "").trim();
          if (/^data:image\//i.test(raw)) return raw;
          return webUrl(raw);
        }
        function collectRecentlyClosed(sessions) {
          const candidates = [];
          (Array.isArray(sessions) ? sessions : []).forEach((session, sessionIndex) => {
            const lastModified = Number(session?.lastModified) || 0;
            if (session?.url && !session?.tab && !session?.window) {
              const directSessionId = String(session.sessionId || "");
              const directSessionType = ["tab", "window", "url"].includes(session.sessionType) ? session.sessionType : directSessionId ? "tab" : "url";
              candidates.push({
                tab: session,
                sessionId: directSessionId,
                sessionType: directSessionType,
                itemId: String(session.id || ""),
                lastModified,
                order: sessionIndex * 1e3
              });
            }
            if (session?.tab) candidates.push({
              tab: session.tab,
              sessionId: String(session.sessionId || session.tab.sessionId || ""),
              sessionType: session.sessionId || session.tab.sessionId ? "tab" : "url",
              lastModified,
              order: sessionIndex * 1e3
            });
            if (Array.isArray(session?.window?.tabs)) {
              const windowSessionId = String(session.sessionId || session.window.sessionId || "");
              session.window.tabs.forEach((tab, tabIndex) => candidates.push({
                tab,
                // A card represents one website, not the whole closed window. Chrome's
                // window session id would restore every tab, so window children reopen
                // their own URL while retaining a stable per-child identity.
                sessionId: "",
                sessionType: "url",
                itemId: windowSessionId ? `session:${windowSessionId}:window:${tabIndex}` : tab?.sessionId ? `session:${tab.sessionId}` : "",
                windowTabIndex: tabIndex,
                lastModified,
                order: sessionIndex * 1e3 + tabIndex + 1
              }));
            }
          });
          candidates.sort((left, right) => right.lastModified - left.lastModified || left.order - right.order);
          const seen = /* @__PURE__ */ new Set();
          const result = [];
          for (const candidate of candidates) {
            const url = webUrl(candidate.tab?.url);
            if (!url) continue;
            const keyUrl = new URL(url);
            keyUrl.hash = "";
            if (seen.has(keyUrl.href)) continue;
            seen.add(keyUrl.href);
            const title = String(candidate.tab?.title || "").trim() || keyUrl.hostname;
            const sessionType = candidate.sessionId ? candidate.sessionType : "url";
            const id = candidate.itemId || (sessionType === "window" ? `session:${candidate.sessionId}:window:${candidate.windowTabIndex}` : candidate.sessionId ? `session:${candidate.sessionId}` : `url:${keyUrl.href}`);
            result.push(Object.freeze({
              id,
              sessionId: candidate.sessionId,
              sessionType,
              title,
              url,
              favIconUrl: faviconUrl(candidate.tab?.favIconUrl),
              lastModified: candidate.lastModified
            }));
          }
          return result;
        }
        function normalizeRecentlyClosed(sessions, limit = MAX_ITEMS) {
          const maximum = Math.max(0, Math.min(MAX_ITEMS, Number(limit) || MAX_ITEMS));
          return collectRecentlyClosed(sessions).slice(0, maximum);
        }
        function recentConsumptionKey(item = {}) {
          const id = String(item.id || "").trim();
          const url = webUrl(item.url);
          if (!id || !url) return "";
          const canonical = new URL(url);
          canonical.hash = "";
          return `${id}
${canonical.href}`;
        }
        return Object.freeze({ MAX_ITEMS, webUrl, collectRecentlyClosed, normalizeRecentlyClosed, recentConsumptionKey });
      });
    }
  });

  // src/shared/browser-adapter.js
  var require_browser_adapter = __commonJS({
    "src/shared/browser-adapter.js"(exports, module) {
      (function attachBrowserAdapter(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaBrowserAdapter = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createBrowserAdapterModule() {
        "use strict";
        function detectBrowserName(userAgent = "") {
          return /\bEdg\//i.test(String(userAgent)) ? "edge" : "chrome";
        }
        function createBrowserAdapter({ root, userAgent } = {}) {
          const scope = root || (typeof globalThis === "object" ? globalThis : {});
          const raw = scope.browser || scope.chrome;
          if (!raw) throw new Error("A Chromium extension API is required");
          return Object.freeze({
            raw,
            runtime: raw.runtime,
            tabs: raw.tabs,
            storage: raw.storage,
            i18n: raw.i18n,
            name: detectBrowserName(userAgent ?? scope.navigator?.userAgent)
          });
        }
        return Object.freeze({ createBrowserAdapter, detectBrowserName });
      });
    }
  });

  // src/shared/i18n.js
  var require_i18n = __commonJS({
    "src/shared/i18n.js"(exports, module) {
      (function attachI18n(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaI18n = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createI18nModule() {
        "use strict";
        const LANGUAGE_STORAGE_KEY = "tfa_language";
        const MESSAGES = Object.freeze({
          zh: Object.freeze({
            "layout.split": "\u53CC\u680F\u603B\u89C8",
            "layout.browser": "\u771F\u5B9E\u9875\u7B7E",
            "layout.tasks": "\u4EFB\u52A1\u603B\u89C8",
            "pane.browser": "\u5F53\u524D\u6D4F\u89C8\u5668\u9875\u7B7E",
            "pane.tasks": "\u4EFB\u52A1\u5212\u5206\u603B\u89C8",
            "search.placeholder": "\u641C\u7D22\u6807\u9898\u3001\u7F51\u7AD9\u6216\u7F51\u5740",
            "filter.allWindows": "\u5168\u90E8\u7A97\u53E3",
            "filter.allDomains": "\u5168\u90E8\u9886\u57DF",
            "filter.allStatuses": "\u5168\u90E8\u72B6\u6001",
            "filter.all": "\u5168\u90E8",
            "locale.id": "zh-CN",
            "task.new": "\u65B0\u589E\u4EFB\u52A1",
            "task.inbox": "\u4E34\u65F6\u533A",
            "inbox.ignorePage": "\u5FFD\u7565\u6B64\u9875\u9762",
            "inbox.markShared": "\u6807\u8BB0\u4E3A\u5171\u4EAB\u5DE5\u5177 / \u591A\u4EFB\u52A1\u9875",
            "inbox.excludeSite": "\u4E0D\u8BB0\u5F55\u6B64\u7AD9\u70B9",
            "inbox.sharedBadge": "\u5171\u4EAB\u5DE5\u5177 / \u591A\u4EFB\u52A1\u9875",
            "toast.inboxPreferenceFailed": "\u4E34\u65F6\u533A\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "empty.tabs": "\u6CA1\u6709\u5339\u914D\u7684\u9875\u7B7E",
            "empty.tasks": "\u8FD8\u6CA1\u6709\u4EFB\u52A1",
            "workbench.layoutLabel": "\u4E3B\u89C6\u56FE\u5207\u6362",
            "workbench.layout.split": "\u53CC\u680F\u603B\u89C8",
            "workbench.layout.browser": "\u771F\u5B9E\u9875\u7B7E",
            "workbench.layout.tasks": "\u4EFB\u52A1\u603B\u89C8",
            "workbench.layout.recycle": "\u56DE\u6536\u7AD9",
            "workbench.searchPlaceholder": "\u641C\u7D22\u9875\u7B7E\u3001\u7F51\u5740\u6216\u4EFB\u52A1",
            "workbench.searchHint": "\u4EFB\u52A1\u6574\u7406\u4E0D\u4F1A\u91CD\u6392\u771F\u5B9E\u9875\u7B7E",
            "workbench.browserTitle": "\u5F53\u524D\u6D4F\u89C8\u5668\u9875\u7B7E",
            "workbench.taskTitle": "\u4EFB\u52A1\u5212\u5206\u603B\u89C8",
            "workbench.inboxTitle": "\u5F85\u5212\u5206\u7684\u9875\u7B7E",
            "workbench.inboxCandidates": "{count} \u4E2A\u5019\u9009",
            "workbench.inboxExpand": "\u5C55\u5F00\u5168\u90E8\u9875\u7B7E",
            "workbench.inboxCollapse": "\u6536\u8D77\u4E3A\u4E00\u884C",
            "inbox.observation.observing.label": "\u89C2\u5BDF\u4E2D",
            "inbox.observation.observing.reason": "\u7B49\u5F85\u66F4\u591A\u6D4F\u89C8\u884C\u4E3A",
            "inbox.observation.temporary_query.label": "\u4E34\u65F6\u67E5\u8BE2",
            "inbox.observation.temporary_query.reason": "\u53EF\u80FD\u662F\u4E00\u6B21\u77ED\u65F6\u67E5\u8BE2",
            "inbox.observation.possible_misclick.label": "\u7591\u4F3C\u8BEF\u70B9",
            "inbox.observation.possible_misclick.reason": "\u505C\u7559\u5F88\u77ED\uFF0C\u6682\u4E0D\u5F52\u7C7B",
            "inbox.observation.redirect_or_ad.label": "\u5E7F\u544A\u8DF3\u8F6C",
            "inbox.observation.redirect_or_ad.reason": "\u7591\u4F3C\u8DF3\u8F6C\u6216\u5E7F\u544A\u9875\u9762",
            "inbox.observation.intent_drift.label": "\u5A31\u4E50\u504F\u822A",
            "inbox.observation.intent_drift.reason": "\u53EF\u80FD\u504F\u79BB\u5F53\u524D\u6D4F\u89C8\u610F\u56FE",
            "workbench.statusFilter": "\u72B6\u6001\u7B5B\u9009",
            "workbench.domainFilter": "\u9886\u57DF\u7B5B\u9009",
            "workbench.taskSort": "\u4EFB\u52A1\u6392\u5E8F",
            "workbench.taskSort.manual": "\u624B\u52A8\u6392\u5E8F",
            "workbench.taskSort.recent": "\u6700\u8FD1\u66F4\u65B0",
            "workbench.taskSort.title": "\u540D\u79F0\u6392\u5E8F",
            "task.view.label": "\u4EFB\u52A1\u89C6\u56FE",
            "task.view.cards": "\u5361\u7247",
            "task.view.aggregate": "\u805A\u5408\u56FE",
            "task.view.fishbone": "\u9C7C\u9AA8\u56FE",
            "task.view.experimentalBadge": "\u5B9E\u9A8C\u89C6\u56FE",
            "task.view.experimentalHint": "\u57FA\u4E8E\u5F53\u524D\u771F\u5B9E\u4EFB\u52A1\u5173\u7CFB\u7684\u53EA\u8BFB\u57FA\u7840\u5C55\u793A\uFF0C\u5B8C\u6574\u5E03\u5C40\u4E0E\u6DF1\u5EA6\u4EA4\u4E92\u540E\u7EED\u5B8C\u5584\u3002",
            "task.view.empty": "\u6682\u65E0\u53EF\u89C6\u5316\u7684\u4EFB\u52A1\u6570\u636E",
            "task.view.emptyHint": "\u65B0\u5EFA\u6216\u5173\u8054\u4EFB\u52A1\u540E\uFF0C\u53EF\u8FD4\u56DE\u6B64\u89C6\u56FE\u67E5\u770B\u3002",
            "task.view.pageCount": "{count} \u4E2A\u5173\u8054\u9875\u9762",
            "task.view.noPages": "\u6682\u65E0\u5173\u8054\u9875\u9762",
            "task.view.aggregateAria": "\u4EFB\u52A1\u805A\u5408\u56FE",
            "task.view.fishboneAria": "\u4EFB\u52A1\u9C7C\u9AA8\u56FE",
            "workbench.allWindows": "\u5168\u90E8\u7A97\u53E3",
            "workbench.allStatuses": "\u5168\u90E8\u72B6\u6001",
            "workbench.allDomains": "\u5168\u90E8\u9886\u57DF",
            "workbench.window": "\u7A97\u53E3 {number}",
            "workbench.tabCount": "{count} \u4E2A\u9875\u7B7E",
            "workbench.untitledTabGroup": "\u672A\u547D\u540D\u6807\u7B7E\u7EC4",
            "workbench.ungroupedTabs": "\u672A\u5206\u7EC4\u9875\u7B7E",
            "workbench.collapseTabGroup": "\u6536\u8D77\u6807\u7B7E\u7EC4",
            "workbench.expandTabGroup": "\u5C55\u5F00\u6807\u7B7E\u7EC4",
            "workbench.tabGroupMore": "\u6807\u7B7E\u7EC4\u64CD\u4F5C",
            "workbench.activateGroupFirst": "\u5207\u6362\u5230\u9996\u4E2A\u9875\u7B7E",
            "workbench.editTabGroup": "\u4FEE\u6539\u540D\u79F0\u548C\u989C\u8272",
            "workbench.ungroupTabs": "\u79FB\u51FA\u6807\u7B7E\u7EC4",
            "workbench.closeTabGroup": "\u5173\u95ED\u6574\u4E2A\u6807\u7B7E\u7EC4",
            "workbench.ungroupTabGroup": "\u89E3\u7EC4",
            "workbench.closeAllTabGroup": "\u5168\u90E8\u5173\u95ED",
            "workbench.groupByDomain": "\u6309\u57DF\u540D\u5206\u7EC4",
            "workbench.ungroupAll": "\u5168\u90E8\u89E3\u7EC4",
            "privacy.title": "\u9690\u79C1\u4E0E\u6570\u636E",
            "privacy.description": "\u67E5\u770B\u672C\u5730\u884C\u4E3A\u8BB0\u5F55\u5E76\u7BA1\u7406\u4FDD\u7559\u6570\u636E",
            "settings.languageTitle": "\u754C\u9762\u8BED\u8A00",
            "settings.languageHint": "\u9ED8\u8BA4\u8DDF\u968F\u6D4F\u89C8\u5668\uFF0C\u4E5F\u53EF\u4EE5\u53EA\u4E3A TabFlow \u624B\u52A8\u5207\u6362\u3002",
            "settings.languageSystem": "\u8DDF\u968F\u6D4F\u89C8\u5668",
            "settings.languageChinese": "\u4E2D\u6587",
            "settings.languageEnglish": "English",
            "settings.languageSaveFailed": "\u8BED\u8A00\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "settings.title": "\u8BBE\u7F6E",
            "settings.navigation": "\u8BBE\u7F6E\u5BFC\u822A",
            "settings.activationTitle": "\u6C38\u4E45\u6FC0\u6D3B",
            "settings.activationDescription": "\u6FC0\u6D3B\u589E\u503C\u6743\u76CA\uFF0C\u514D\u8D39\u6838\u5FC3\u529F\u80FD\u59CB\u7EC8\u53EF\u7528",
            "settings.dockTitle": "\u7F51\u9875\u62D3\u5C55\u575E",
            "settings.dockDescription": "\u7BA1\u7406\u56FA\u5B9A\u7F51\u7AD9\u3001\u5916\u89C2\u4E0E\u4EA4\u4E92",
            "settings.close": "\u5173\u95ED\u8BBE\u7F6E",
            "privacy.localBehavior": "\u672C\u5730\u884C\u4E3A\u8BB0\u5F55",
            "privacy.localBehaviorHint": "\u53EA\u5728\u672C\u673A\u8BB0\u5F55\u6253\u5F00\u3001\u5207\u6362\u3001\u8DF3\u8F6C\u3001\u5173\u95ED\u7B49\u5FC5\u8981\u9875\u7B7E\u884C\u4E3A\uFF0C\u6700\u957F\u4FDD\u7559 30 \u5929\u3002\u6570\u636E\u4E0D\u4F1A\u4E0A\u4F20\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u6539\u53D8\u4EFB\u52A1\u5173\u7CFB\u6216\u771F\u5B9E\u9875\u7B7E\u987A\u5E8F\u3002",
            "privacy.behaviorClearHint": "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9\u4E0D\u4F1A\u5220\u9664\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u9875\u9762\u8D44\u6599\u6216\u5F53\u524D\u9875\u7B7E\u5B9E\u4F8B\u3002",
            "privacy.dataControls": "\u6570\u636E\u7BA1\u7406",
            "privacy.exportHint": "\u5B89\u5168\u5BFC\u51FA\u5305\u542B\u4EFB\u52A1\u3001\u9875\u9762\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u65B0\u9875\u5E38\u7528\u7F51\u7AD9\u548C\u7F51\u9875\u62D3\u5C55\u575E\uFF1B\u4E0D\u5305\u542B API \u5BC6\u94A5\u3001\u6FC0\u6D3B\u4FE1\u606F\u3001\u622A\u56FE\u3001Cookie\u3001\u884C\u4E3A\u539F\u59CB\u4E0A\u4E0B\u6587\u6216\u5B8C\u6574\u7F51\u5740\u53C2\u6570\u3002",
            "privacy.export": "\u5B89\u5168\u5BFC\u51FA",
            "privacy.clearBehavior": "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9",
            "privacy.clearAi": "\u6E05\u9664 AI \u6570\u636E",
            "privacy.clearAll": "\u6E05\u9664\u5168\u90E8\u672C\u5730\u6570\u636E",
            "privacy.clearing": "\u6B63\u5728\u6E05\u9664\u2026",
            "privacy.clearAiConfirm": "\u786E\u5B9A\u6E05\u9664\u672C\u673A AI \u8BBE\u7F6E\u3001API \u5BC6\u94A5\u548C\u8FD0\u884C\u72B6\u6001\u5417\uFF1F\u4EFB\u52A1\u548C\u4EFB\u52A1\u5173\u7CFB\u4E0D\u4F1A\u88AB\u5220\u9664\u3002",
            "privacy.clearBehaviorConfirm": "\u786E\u5B9A\u6E05\u9664\u672C\u673A\u6700\u8FD1 30 \u5929\u7684\u884C\u4E3A\u8F68\u8FF9\u5417\uFF1F\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u9875\u9762\u8D44\u6599\u548C\u5F53\u524D\u9875\u7B7E\u4E0D\u4F1A\u88AB\u5220\u9664\u3002",
            "privacy.clearBehaviorSuccess": "\u5DF2\u6E05\u9664 {count} \u6761\u884C\u4E3A\u8BB0\u5F55\uFF1B\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u548C\u5F53\u524D\u9875\u7B7E\u4FDD\u6301\u4E0D\u53D8\u3002",
            "privacy.clearBehaviorFailed": "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "privacy.clearAllConfirm": "\u786E\u5B9A\u6E05\u9664\u5168\u90E8 TabFlow \u672C\u5730\u6570\u636E\u5417\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u884C\u4E3A\u8BB0\u5F55\u548C AI \u5BC6\u94A5\uFF0C\u4E14\u65E0\u6CD5\u64A4\u9500\u3002",
            "privacy.clearAiSuccess": "\u5DF2\u6E05\u9664\u672C\u673A AI \u6570\u636E\uFF1B\u4EFB\u52A1\u548C\u4EFB\u52A1\u5173\u7CFB\u4FDD\u6301\u4E0D\u53D8\u3002",
            "privacy.clearAllSuccess": "\u5DF2\u6E05\u9664\u5168\u90E8 TabFlow \u672C\u5730\u6570\u636E\u3002",
            "privacy.exportSuccess": "\u5B89\u5168\u6570\u636E\u5DF2\u5BFC\u51FA\u3002",
            "privacy.actionFailed": "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "privacy.exportFailed": "\u5BFC\u51FA\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "privacy.backup.title": "\u5907\u4EFD\u4E0E\u6062\u590D",
            "privacy.backup.hint": "\u9009\u62E9 TabFlow JSON \u5907\u4EFD\u540E\u4F1A\u5148\u663E\u793A\u5B89\u5168\u6458\u8981\uFF1B\u786E\u8BA4\u524D\u4E0D\u4F1A\u8986\u76D6\u4EFB\u4F55\u6570\u636E\u3002\u6587\u4EF6\u6700\u5927 5 MiB\u3002",
            "privacy.backup.reinstallWarning": "\u5347\u7EA7\u6216\u91CD\u65B0\u52A0\u8F7D\u4F1A\u4FDD\u7559\u672C\u5730\u6570\u636E\uFF1B\u5378\u8F7D\u6269\u5C55\u901A\u5E38\u4F1A\u5220\u9664\u672C\u5730\u6570\u636E\u3002\u5378\u8F7D\u524D\u8BF7\u5148\u5BFC\u51FA\u5B89\u5168\u5907\u4EFD\uFF0C\u91CD\u88C5\u540E\u518D\u4ECE\u5907\u4EFD\u6062\u590D\u3002",
            "privacy.backup.choose": "\u4ECE\u5907\u4EFD\u6062\u590D",
            "privacy.backup.reading": "\u6B63\u5728\u6821\u9A8C\u2026",
            "privacy.backup.previewTitle": "\u6062\u590D\u5185\u5BB9\u9884\u89C8",
            "privacy.backup.legacy": "\u65E7\u7248\u5907\u4EFD\uFF1A\u53EA\u4F1A\u8986\u76D6\u4EFB\u52A1\u3001\u9875\u9762\u548C\u4EFB\u52A1\u5173\u7CFB\uFF0C\u4E0D\u4F1A\u8986\u76D6\u65B0\u9875\u5E38\u7528\u7F51\u7AD9\u6216\u7F51\u9875\u62D3\u5C55\u575E\u3002",
            "privacy.backup.section.tasks": "\u4EFB\u52A1\u6570\u636E",
            "privacy.backup.section.newtab": "\u65B0\u9875\u5E38\u7528\u7F51\u7AD9",
            "privacy.backup.section.dock": "\u7F51\u9875\u62D3\u5C55\u575E",
            "privacy.backup.section.none": "\u65E0\u53EF\u6062\u590D\u6570\u636E",
            "privacy.backup.willOverwrite": "\u5C06\u8986\u76D6\uFF1A{sections}",
            "privacy.backup.count.tasks": "\u4EFB\u52A1",
            "privacy.backup.count.pages": "\u9875\u9762",
            "privacy.backup.count.relations": "\u4EFB\u52A1\u5173\u7CFB",
            "privacy.backup.count.categories": "\u65B0\u9875\u5206\u7C7B",
            "privacy.backup.count.sites": "\u5E38\u7528\u7F51\u7AD9",
            "privacy.backup.count.dockSites": "\u62D3\u5C55\u575E\u7F51\u7AD9",
            "privacy.backup.overwriteWarning": "\u6062\u590D\u4F1A\u8986\u76D6\u4E0A\u8FF0\u672C\u5730\u6570\u636E\uFF1B\u4E0D\u4F1A\u6062\u590D API \u5BC6\u94A5\u3001\u6FC0\u6D3B\u4FE1\u606F\u3001\u6D4F\u89C8\u8BB0\u5F55\u6216\u5F53\u524D\u771F\u5B9E\u9875\u7B7E\u3002",
            "privacy.backup.cancel": "\u53D6\u6D88",
            "privacy.backup.confirm": "\u8986\u76D6\u5E76\u6062\u590D",
            "privacy.backup.importing": "\u6B63\u5728\u6062\u590D\u2026",
            "privacy.backup.importSuccess": "\u6062\u590D\u5B8C\u6210\uFF0C\u4EFB\u52A1\u3001\u65B0\u9875\u548C\u7F51\u9875\u62D3\u5C55\u575E\u5DF2\u540C\u6B65\u3002",
            "privacy.backup.error.tooLarge": "\u5907\u4EFD\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 5 MiB\u3002",
            "privacy.backup.error.invalidJson": "\u6240\u9009\u6587\u4EF6\u4E0D\u662F\u6709\u6548\u7684 JSON \u5907\u4EFD\u3002",
            "privacy.backup.error.invalidFormat": "\u6240\u9009\u6587\u4EF6\u4E0D\u662F\u53D7\u652F\u6301\u7684 TabFlow \u5907\u4EFD\u3002",
            "privacy.backup.error.unsupportedVersion": "\u8BE5\u5907\u4EFD\u7248\u672C\u6682\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u5347\u7EA7 TabFlow \u540E\u91CD\u8BD5\u3002",
            "privacy.backup.error.sensitiveData": "\u4E3A\u4FDD\u62A4\u9690\u79C1\uFF0C\u8BE5\u6587\u4EF6\u5305\u542B\u4E0D\u5141\u8BB8\u6062\u590D\u7684\u654F\u611F\u6570\u636E\u3002",
            "privacy.backup.error.unknownField": "\u5907\u4EFD\u5305\u542B\u5F53\u524D\u7248\u672C\u4E0D\u652F\u6301\u7684\u6570\u636E\u5B57\u6BB5\u3002",
            "privacy.backup.error.invalidContent": "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002",
            "privacy.backup.error.duplicate": "\u5907\u4EFD\u4E2D\u5B58\u5728\u91CD\u590D\u8BB0\u5F55\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002",
            "privacy.backup.error.brokenReference": "\u5907\u4EFD\u4E2D\u7684\u4EFB\u52A1\u5173\u7CFB\u5F15\u7528\u7F3A\u5931\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002",
            "privacy.backup.error.primaryConflict": "\u5907\u4EFD\u4E2D\u540C\u4E00\u9875\u9762\u5B58\u5728\u591A\u4E2A\u4E3B\u8981\u4EFB\u52A1\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002",
            "privacy.backup.error.sectionUnavailable": "\u5F53\u524D\u7248\u672C\u65E0\u6CD5\u6062\u590D\u8BE5\u6587\u4EF6\u5305\u542B\u7684\u90E8\u5206\u6570\u636E\uFF1B\u5C1A\u672A\u8FDB\u884C\u4EFB\u4F55\u66F4\u6539\u3002",
            "privacy.backup.error.confirmationRequired": "\u8BF7\u5148\u660E\u786E\u786E\u8BA4\u8986\u76D6\u672C\u5730\u6570\u636E\u3002",
            "privacy.backup.error.rolledBack": "\u6062\u590D\u5931\u8D25\uFF0C\u539F\u6709\u6570\u636E\u5DF2\u6062\u590D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "privacy.backup.error.partialRollback": "\u6062\u590D\u5931\u8D25\uFF0C\u90E8\u5206\u539F\u6709\u6570\u636E\u53EF\u80FD\u672A\u80FD\u8FD8\u539F\u3002\u8BF7\u505C\u6B62\u7F16\u8F91\u3001\u4FDD\u7559\u5907\u4EFD\u5E76\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002",
            "privacy.backup.error.inProgress": "\u53E6\u4E00\u9879\u6062\u590D\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u91CD\u8BD5\u3002",
            "privacy.backup.error.previewRequired": "\u6062\u590D\u9884\u89C8\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u5907\u4EFD\u6587\u4EF6\u5E76\u518D\u6B21\u786E\u8BA4\u3002",
            "privacy.backup.error.reconcileFailed": "\u6570\u636E\u5DF2\u5199\u5165\uFF0C\u4F46\u5F53\u524D\u9875\u7B7E\u72B6\u6001\u540C\u6B65\u5931\u8D25\u3002\u8BF7\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u540E\u6838\u5BF9\u3002",
            "privacy.backup.error.unavailable": "\u5907\u4EFD\u6062\u590D\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "privacy.backup.error.failed": "\u6062\u590D\u5907\u4EFD\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "privacy.backup.error.readFailed": "\u65E0\u6CD5\u8BFB\u53D6\u6240\u9009\u6587\u4EF6\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002",
            "workbench.taskPages": "{count} \u4E2A\u76F8\u5173\u9875\u9762",
            "workbench.globalCount": "{tabs} \u4E2A\u9875\u7B7E \xB7 {tasks} \u4E2A\u4EFB\u52A1",
            "workbench.localReady": "\u672C\u5730\u5DE5\u4F5C\u53F0\u5DF2\u5C31\u7EEA",
            "workbench.footerHint": "\u70B9\u51FB\u9875\u7B7E\u5207\u6362 \xB7 \u641C\u7D22\u540C\u6B65\u8FC7\u6EE4\u4E24\u680F",
            "workbench.unknownPage": "\u672A\u77E5\u9875\u9762",
            "workbench.sessionDwell": "\u505C\u7559 {time}",
            "workbench.pageOpenNow": "\u6D4F\u89C8\u5668\u4E2D\u5DF2\u6253\u5F00",
            "workbench.pageHistory": "\u5386\u53F2\u9875\u9762\uFF0C\u70B9\u51FB\u91CD\u65B0\u6253\u5F00",
            "workbench.previewImageAlt": "\u9875\u9762\u9884\u89C8",
            "workbench.previewLoading": "\u6B63\u5728\u52A0\u8F7D\u5B89\u5168\u9884\u89C8\u2026",
            "workbench.previewUnavailable": "\u6682\u65E0\u53EF\u7528\u9884\u89C8",
            "workbench.previewRestricted": "\u6D4F\u89C8\u5668\u9650\u5236\u9875\u9762\u65E0\u6CD5\u751F\u6210\u9884\u89C8",
            "workbench.previewActivateRequired": "\u5207\u6362\u5230\u6B64\u9875\u7B7E\u5E76\u7B49\u5F85\u52A0\u8F7D\u540E\uFF0C\u5C06\u81EA\u52A8\u751F\u6210\u9884\u89C8",
            "workbench.previewWaitingForLoad": "\u9875\u9762\u52A0\u8F7D\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u751F\u6210\u9884\u89C8",
            "workbench.openNow": "\u5F53\u524D\u5DF2\u6253\u5F00",
            "workbench.previewSwitchHint": "\u70B9\u51FB\u5361\u7247\u5207\u6362",
            "workbench.columns": "\u5217\u6570",
            "workbench.columnsAria": "\u6BCF\u884C\u9875\u7B7E\u6570\u91CF",
            "workbench.columnsValue": "{count} \u5217",
            "workbench.noTabs": "\u6CA1\u6709\u5339\u914D\u7684\u9875\u7B7E",
            "workbench.noTabsHint": "\u6362\u4E00\u4E2A\u5173\u952E\u8BCD\u6216\u7A97\u53E3\u8BD5\u8BD5",
            "workbench.noTasks": "\u6CA1\u6709\u5339\u914D\u7684\u4EFB\u52A1",
            "workbench.noTasksHint": "\u8C03\u6574\u641C\u7D22\u3001\u9886\u57DF\u6216\u72B6\u6001\u7B5B\u9009",
            "workbench.emptyTask": "\u4EFB\u52A1\u4E2D\u8FD8\u6CA1\u6709\u9875\u9762",
            "workbench.emptyTaskHint": "\u540E\u7EED\u53EF\u4ECE\u771F\u5B9E\u9875\u7B7E\u62D6\u5165",
            "workbench.recycleTitle": "\u56DE\u6536\u7AD9",
            "workbench.resizePanes": "\u8C03\u6574\u53CC\u680F\u5BBD\u5EA6",
            "workbench.collapseBrowserPane": "\u6536\u8D77\u771F\u5B9E\u9875\u7B7E\u680F",
            "workbench.collapseTaskPane": "\u6536\u8D77\u4EFB\u52A1\u603B\u89C8\u680F",
            "workbench.restoreSplitPane": "\u6062\u590D\u53CC\u680F\u603B\u89C8",
            "workbench.recycleHint": "\u6309\u5173\u95ED\u65F6\u95F4\u6392\u5217\uFF0C\u53EF\u6062\u590D\u9875\u7B7E\u3001\u9875\u7B7E\u7EC4\u548C\u4EFB\u52A1",
            "recycle.restoreAll": "\u6062\u590D\u5168\u90E8",
            "recycle.recently": "\u521A\u521A",
            "recycle.minutesAgo": "{count} \u5206\u949F\u524D",
            "recycle.today": "\u4ECA\u5929",
            "recycle.group": "\u9875\u7B7E\u7EC4",
            "recycle.task": "\u4EFB\u52A1\u7EC4",
            "recycle.closedTab": "\u5DF2\u5173\u95ED\u9875\u7B7E",
            "recycle.restore": "\u6062\u590D",
            "recycle.delete": "\u5220\u9664",
            "recycle.deletePermanently": "\u6C38\u4E45\u5220\u9664",
            "workbench.recycleRestoreAll": "\u6062\u590D\u5168\u90E8",
            "workbench.recycleEmpty": "\u56DE\u6536\u7AD9\u4E3A\u7A7A",
            "workbench.recycleEmptyHint": "\u5173\u95ED\u7684\u9875\u7B7E\u3001\u9875\u7B7E\u7EC4\u548C\u89E3\u6563\u7684\u4EFB\u52A1\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC",
            "workbench.recycleTask": "\u4EFB\u52A1\u7EC4",
            "workbench.recycleTab": "\u9875\u7B7E",
            "workbench.recycleGroup": "\u9875\u7B7E\u7EC4",
            "workbench.recycleRestore": "\u6062\u590D",
            "workbench.recycleDelete": "\u6C38\u4E45\u5220\u9664",
            "common.close": "\u5173\u95ED",
            "common.theme": "\u5207\u6362\u76AE\u80A4",
            "common.settings": "\u8BBE\u7F6E",
            "theme.title": "\u4E3B\u9898\u5916\u89C2",
            "theme.system": "\u8DDF\u968F\u7CFB\u7EDF",
            "theme.light": "\u6D45\u8272",
            "theme.dark": "\u6DF1\u8272",
            "theme.changed": "\u5DF2\u5207\u6362\u4E3A{theme}",
            "overlay.sticky": "\u5DF2\u56FA\u5B9A",
            "overlay.title": "\u9875\u7B7E\u5DE5\u4F5C\u53F0",
            "overlay.subtitle": "\u641C\u7D22\u3001\u6574\u7406\u3001\u5206\u7EC4\u3001\u6062\u590D\u4E0E\u9884\u89C8\u90FD\u96C6\u4E2D\u5728\u8FD9\u91CC",
            "overlay.membership": "\u4F1A\u5458\u72B6\u6001",
            "overlay.activated": "\u5DF2\u6C38\u4E45\u6FC0\u6D3B",
            "overlay.close": "\u5173\u95ED\u6D6E\u5C42 (Esc)",
            "overlay.searchPlaceholder": "\u641C\u7D22\u9875\u7B7E\u6807\u9898\u3001\u57DF\u540D\u6216\u7F51\u5740",
            "overlay.selectedNone": "\u5DF2\u9009 0",
            "overlay.action.newTitle": "\u65B0\u5EFA\u9875\u7B7E\uFF08\u70ED\u95E8\u4E66\u7B7E\uFF09",
            "overlay.action.new": "\u65B0\u5EFA",
            "overlay.action.groupDomainsTitle": "\u6309\u4E3B\u57DF\u540D\u4E00\u952E\u5206\u7EC4\u6240\u6709\u9875\u7B7E",
            "overlay.action.groupDomains": "\u57DF\u540D\u5206\u7EC4",
            "overlay.action.ungroupAllTitle": "\u89E3\u6563\u6240\u6709\u5206\u7EC4",
            "overlay.action.ungroupAll": "\u89E3\u7EC4",
            "overlay.action.recycleTitle": "\u67E5\u770B\u56DE\u6536\u7AD9",
            "overlay.action.recycle": "\u56DE\u6536\u7AD9",
            "overlay.action.undoTitle": "\u5173\u95ED\u56DE\u64A4\uFF08\u6062\u590D\u6700\u8FD1\u4E00\u4E2A\uFF09",
            "overlay.action.undo": "\u56DE\u64A4",
            "overlay.footer.escapeClose": "\u5173\u95ED",
            "overlay.footer.holdOpen": "\u6309\u4F4F\u5FEB\u6377\u952E\u5524\u8D77",
            "overlay.footer.stickyBefore": "\u8D85\u8FC7",
            "overlay.footer.stickyAfter": "\u79D2\u677E\u5F00\u4FDD\u6301",
            "overlay.footer.holdDuration": "\u6309\u4F4F\u65F6\u957F",
            "overlay.footer.hints": "\u70B9\u51FB\u5207\u6362 \xB7 \u60AC\u505C\u9884\u89C8 \xB7 \u62D6\u52A8\u6392\u5E8F \xB7 \u53F3\u952E\u591A\u9009",
            "overlay.layout.aria": "\u9875\u7B7E\u5E03\u5C40\u8BBE\u7F6E",
            "overlay.layout.perRow": "\u6BCF\u884C",
            "overlay.layout.perRowAria": "\u6BCF\u884C\u9875\u7B7E\u6570\u91CF",
            "overlay.layout.columns": "{count}\u5217",
            "overlay.layout.viewAria": "\u9875\u7B7E\u89C6\u56FE",
            "overlay.layout.hover": "\u60AC\u6D6E",
            "overlay.layout.card": "\u5361\u7247",
            "overlay.layout.hoverPreview": "\u60AC\u505C\u9884\u89C8",
            "overlay.selection.groupTitle": "\u5C06\u9009\u4E2D\u9875\u7B7E\u5F52\u5165\u65B0\u5206\u7EC4",
            "overlay.selection.group": "\u5206\u7EC4\u9009\u4E2D",
            "overlay.selection.closeTitle": "\u6279\u91CF\u5173\u95ED\u9009\u4E2D",
            "overlay.selection.close": "\u6279\u91CF\u5173\u95ED",
            "overlay.selection.clearTitle": "\u53D6\u6D88\u9009\u62E9",
            "overlay.selection.clear": "\u53D6\u6D88",
            "overlay.loading": "\u52A0\u8F7D\u4E2D\u2026",
            "overlay.count.recycle": "\u56DE\u6536\u7AD9 {count} \u9879",
            "overlay.count.tabs": "{count} \u4E2A\u6807\u7B7E",
            "overlay.count.windows": "{count} \u4E2A\u7A97\u53E3",
            "overlay.count.selected": "\u5DF2\u9009 {selected} / {total}",
            "dock.label": "\u7F51\u9875\u62D3\u5C55\u575E",
            "dock.expand": "\u5C55\u5F00\u7F51\u9875\u62D3\u5C55\u575E",
            "dock.addSite": "\u65B0\u589E\u7F51\u7AD9",
            "dock.search": "\u7F51\u9875\u641C\u7D22",
            "dock.settings": "\u7F51\u9875\u62D3\u5C55\u575E\u8BBE\u7F6E",
            "dock.settingsShort": "\u62D3\u5C55\u575E\u8BBE\u7F6E",
            "dock.settings.empty": "\u6682\u65E0\u56FA\u5B9A\u7F51\u7AD9",
            "dock.settings.fixedSites": "\u56FA\u5B9A\u7F51\u7AD9",
            "dock.settings.iconSize": "\u56FE\u6807\u5927\u5C0F",
            "dock.settings.magnification": "\u60AC\u505C\u653E\u5927",
            "dock.settings.influenceRadius": "\u5F71\u54CD\u8303\u56F4",
            "dock.settings.response": "\u54CD\u5E94\u901F\u5EA6",
            "dock.settings.damping": "\u963B\u5C3C",
            "dock.settings.hideDelay": "\u9690\u85CF\u5EF6\u8FDF",
            "dock.settings.tileSize": "\u56FE\u6807\u5E95\u677F\u5927\u5C0F",
            "dock.settings.trayPadding": "\u6258\u76D8\u7559\u767D",
            "dock.settings.tileOpacity": "\u5E95\u677F\u4E0D\u900F\u660E\u5EA6",
            "dock.settings.tileColor": "\u56FE\u6807\u5E95\u677F\u989C\u8272",
            "dock.settings.enabled": "\u542F\u7528\u7F51\u9875\u62D3\u5C55\u575E",
            "dock.settings.enabledHint": "\u5728\u6240\u6709\u7F51\u9875\u5E95\u90E8\u663E\u793A\u5E38\u7528\u7F51\u7AD9",
            "dock.settings.autoHide": "\u81EA\u52A8\u9690\u85CF",
            "dock.settings.autoHideHint": "\u9F20\u6807\u79BB\u5F00\u540E\u81EA\u52A8\u6536\u8D77\u62D3\u5C55\u575E",
            "dock.settings.appearance": "\u5916\u89C2",
            "dock.settings.motion": "\u52A8\u6548",
            "dock.settings.preview": "\u9884\u89C8",
            "dock.settings.previewAria": "\u7F51\u9875\u62D3\u5C55\u575E\u9884\u89C8",
            "dock.settings.reset": "\u6062\u590D\u9ED8\u8BA4",
            "dock.settings.siteName": "\u7F51\u7AD9\u540D\u79F0",
            "dock.settings.siteUrl": "\u7F51\u7AD9\u5730\u5740",
            "dock.settings.addSite": "\u6DFB\u52A0\u7F51\u7AD9",
            "dock.settings.drag": "\u62D6\u52A8\u6392\u5E8F",
            "dock.settings.save": "\u4FDD\u5B58",
            "dock.settings.delete": "\u5220\u9664",
            "dock.settings.platformNote": "\u63D0\u793A\uFF1A\u90E8\u5206\u7F51\u7AD9\u4F1A\u9650\u5236\u8BFB\u53D6\u56FE\u6807\uFF0CTabFlow \u4F1A\u81EA\u52A8\u4F7F\u7528\u5B89\u5168\u66FF\u4EE3\u56FE\u6807\u3002",
            "dock.settings.saveHint": "\u4FEE\u6539\u4F1A\u5B9E\u65F6\u9884\u89C8\uFF0C\u70B9\u51FB\u5B8C\u6210\u540E\u5173\u95ED\u8BBE\u7F6E\u3002",
            "dock.settings.done": "\u5B8C\u6210",
            "dock.settings.updated": "\u7F51\u7AD9\u5DF2\u66F4\u65B0",
            "dock.settings.added": "\u7F51\u7AD9\u5DF2\u6DFB\u52A0",
            "dock.settings.saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.settings.addFailed": "\u6DFB\u52A0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.settings.saveDirtyFirst": "\u8BF7\u5148\u4FDD\u5B58\u5F53\u524D\u7F16\u8F91\u9879",
            "dock.recent": "\u6700\u8FD1\u5173\u95ED",
            "dock.recentSites": "\u6700\u8FD1\u5173\u95ED\u7684\u7F51\u7AD9",
            "dock.recentLoading": "\u6B63\u5728\u8BFB\u53D6\u6700\u8FD1\u5173\u95ED\u2026",
            "dock.recentEmpty": "\u6682\u65E0\u53EF\u6062\u590D\u7684\u7F51\u7AD9",
            "dock.recentUnavailable": "\u6700\u8FD1\u5173\u95ED\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6",
            "dock.recentRestoreAria": "\u6062\u590D {title}",
            "dock.recentRestoreFailed": "\u6062\u590D\u7F51\u7AD9\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.recentSyncFailed": "\u7F51\u7AD9\u5DF2\u6253\u5F00\uFF0C\u4F46\u6700\u8FD1\u5173\u95ED\u5217\u8868\u540C\u6B65\u5931\u8D25",
            "dock.recentRestored": "\u5DF2\u6062\u590D\u7F51\u7AD9",
            "dock.menu.background": "\u5728\u540E\u53F0\u65B0\u9875\u6253\u5F00",
            "dock.menu.edit": "\u7F16\u8F91",
            "dock.menu.delete": "\u5220\u9664",
            "dock.openBackgroundFailed": "\u540E\u53F0\u6253\u5F00\u7F51\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.openFailed": "\u6253\u5F00\u7F51\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.deleteFailed": "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.deleted": "\u7F51\u9875\u5DF2\u5220\u9664",
            "dock.orderSaved": "\u7F51\u9875\u987A\u5E8F\u5DF2\u4FDD\u5B58",
            "dock.orderSaveFailed": "\u6392\u5E8F\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.pinFailed": "\u56FA\u5B9A\u72B6\u6001\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.pinned": "\u5DF2\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            "dock.unpinned": "\u5DF2\u53D6\u6D88\u56FA\u5B9A",
            "dock.extensionUpdated": "\u6269\u5C55\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u540E\u91CD\u8BD5",
            "dock.duplicate": "\u8BE5\u7F51\u7AD9\u5DF2\u7ECF\u5728\u7F51\u9875\u62D3\u5C55\u575E\u4E2D",
            "activation.locale": "zh-CN",
            "activation.localRecord": "\u672C\u673A\u8BB0\u5F55",
            "activation.loadFailed": "\u8BFB\u53D6\u6FC0\u6D3B\u72B6\u6001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "activation.purchaseGuide": "\u95F2\u9C7C\u8D2D\u4E70\u5F15\u5BFC\u56FE",
            "activation.purchaseActiveHint": "\u6FC0\u6D3B\u540E\u9ED8\u8BA4\u6298\u53E0\uFF0C\u4ECD\u53EF\u91CD\u65B0\u5C55\u5F00",
            "activation.purchaseHint": "\u626B\u7801\u8D2D\u4E70\u6C38\u4E45\u6FC0\u6D3B\u7801",
            "activation.expandImage": "\u5C55\u5F00\u56FE\u7247",
            "activation.purchaseAria": "\u8D2D\u4E70\u6FC0\u6D3B\u7801",
            "activation.zoomAria": "\u653E\u5927\u67E5\u770B\u95F2\u9C7C\u8D2D\u4E70\u5F15\u5BFC\u56FE",
            "activation.imageAlt": "\u95F2\u9C7C\u5E97\u94FA\u8D2D\u4E70\u5F15\u5BFC\u53CA\u4E8C\u7EF4\u7801",
            "activation.purchaseEyebrow": "\u8D2D\u4E70\u6307\u5F15",
            "activation.noCode": "\u8FD8\u6CA1\u6709\u6FC0\u6D3B\u7801\uFF1F",
            "activation.purchaseCopy": "\u4FDD\u5B58\u6216\u653E\u5927\u56FE\u7247\u540E\uFF0C\u4F7F\u7528\u95F2\u9C7C\u626B\u4E00\u626B\u8FDB\u5165\u5E97\u94FA\u8D2D\u4E70\u3002\u8D2D\u4E70\u5B8C\u6210\u540E\u4F1A\u6536\u5230\u6C38\u4E45\u6FC0\u6D3B\u7801\u3002",
            "activation.zoom": "\u653E\u5927\u67E5\u770B",
            "activation.collapseImage": "\u6536\u8D77\u56FE\u7247",
            "activation.memberStatus": "\u4F1A\u5458\u72B6\u6001",
            "activation.active": "\u5DF2\u6C38\u4E45\u6FC0\u6D3B",
            "activation.unlocked": "\u589E\u503C\u529F\u80FD\u5DF2\u89E3\u9501",
            "activation.used": "\u8BE5\u6FC0\u6D3B\u7801\u5DF2\u4F7F\u7528\u5E76\u4F5C\u5E9F",
            "activation.activatedAt": "\u6FC0\u6D3B\u65F6\u95F4\uFF1A{time}",
            "activation.deactivate": "\u6CE8\u9500\u6FC0\u6D3B",
            "activation.invalid": "\u6FC0\u6D3B\u7801\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u8F93\u5165",
            "activation.eyebrow": "\u6C38\u4E45\u6FC0\u6D3B",
            "activation.inputTitle": "\u8F93\u5165\u8D2D\u4E70\u540E\u6536\u5230\u7684\u6FC0\u6D3B\u7801",
            "activation.copy": "\u65E0\u9700\u767B\u5F55\uFF0C\u8054\u7F51\u540E\u5373\u53EF\u9A8C\u8BC1\u3002\u672C\u673A\u5C06\u6C38\u4E45\u4FDD\u5B58\u6FC0\u6D3B\u72B6\u6001\uFF0C\u91CD\u88C5\u540E\u53EF\u518D\u6B21\u8F93\u5165\u539F\u6FC0\u6D3B\u7801\u6062\u590D\u3002",
            "activation.code": "\u6FC0\u6D3B\u7801",
            "activation.verifying": "\u6B63\u5728\u9A8C\u8BC1\u2026",
            "activation.submit": "\u9A8C\u8BC1\u5E76\u6FC0\u6D3B",
            "activation.progress": "\u6B63\u5728\u8FDE\u63A5\u5E76\u9A8C\u8BC1\u6FC0\u6D3B\u7801\uFF0C\u8BF7\u7A0D\u5019",
            "activation.offline": "\u7F51\u7EDC\u4E0D\u53EF\u7528\uFF0C\u8BF7\u8FDE\u63A5\u7F51\u7EDC\u540E\u91CD\u8BD5",
            "activation.failed": "\u6FC0\u6D3B\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "activation.previewAria": "\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247\u9884\u89C8",
            "activation.previewClose": "\u5173\u95ED\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247",
            "activation.deactivateClose": "\u5173\u95ED\u6CE8\u9500\u786E\u8BA4",
            "activation.deactivateTitle": "\u786E\u8BA4\u6CE8\u9500\u6FC0\u6D3B\uFF1F",
            "activation.deactivateCopy": "\u6CE8\u9500\u540E\u4F1A\u5458\u6807\u8BC6\u53CA\u5168\u90E8\u589E\u503C\u6743\u76CA\u5C06\u7ACB\u5373\u5931\u6548\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u4F7F\u7528\u540C\u4E00\u6FC0\u6D3B\u7801\u91CD\u65B0\u6FC0\u6D3B\u3002",
            "activation.cancel": "\u53D6\u6D88",
            "activation.confirmDeactivate": "\u786E\u8BA4\u6CE8\u9500",
            "activation.deactivating": "\u6B63\u5728\u6CE8\u9500\u2026",
            "activation.deactivated": "\u5DF2\u6CE8\u9500\u6FC0\u6D3B",
            "activation.deactivateFailed": "\u6CE8\u9500\u6FC0\u6D3B\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.add.catalogQuick": "\u5FEB\u6377\u9009\u62E9\u5E38\u7528\u7F51\u7AD9",
            "dock.add.siteCount": "{count} \u4E2A",
            "dock.add.catalogSearchPlaceholder": "\u641C\u7D22\u7F51\u7AD9\u6216\u5206\u7C7B",
            "dock.add.catalogCategories": "\u5E38\u7528\u7F51\u7AD9\u5206\u7C7B",
            "dock.add.editTitle": "\u7F16\u8F91\u7F51\u7AD9",
            "dock.add.addTitle": "\u65B0\u589E\u7F51\u7AD9",
            "dock.add.editDescription": "\u4FEE\u6539\u7F51\u7AD9\u6807\u9898\u3001\u7F51\u5740\u548C\u56FE\u6807",
            "dock.add.addDescription": "\u4FDD\u5B58\u5F53\u524D\u9875\u9762\uFF0C\u6216\u6DFB\u52A0\u4EFB\u610F\u5E38\u7528\u7F51\u7AD9",
            "dock.add.close": "\u5173\u95ED\u65B0\u589E\u7F51\u7AD9\u5F39\u7A97",
            "dock.add.modeLabel": "\u65B0\u589E\u65B9\u5F0F",
            "dock.add.mode.current": "\u4FDD\u5B58\u5F53\u524D\u7F51\u9875",
            "dock.add.mode.custom": "\u81EA\u5B9A\u4E49\u65B0\u589E",
            "dock.add.mode.catalog": "\u9009\u62E9\u5E38\u7528\u7F51\u7AD9",
            "dock.add.mode.bookmarks": "\u4ECE\u4E66\u7B7E\u5BFC\u5165",
            "dock.add.bookmarksHeading": "\u9009\u62E9 Chrome \u4E66\u7B7E",
            "dock.add.bookmarkCount": "{count} \u4E2A",
            "dock.add.bookmarkSelected": "\u5DF2\u9009 {count} \u4E2A",
            "dock.add.bookmarkSearchPlaceholder": "\u641C\u7D22\u6807\u9898\u3001\u7F51\u5740\u6216\u6587\u4EF6\u5939",
            "dock.add.bookmarksLoading": "\u6B63\u5728\u8BFB\u53D6 Chrome \u4E66\u7B7E\u2026",
            "dock.add.bookmarksList": "Chrome \u4E66\u7B7E\u5217\u8868",
            "dock.add.bookmarksNoMatch": "\u6CA1\u6709\u5339\u914D\u7684\u4E66\u7B7E",
            "dock.add.bookmarksEmpty": "\u6682\u65E0\u53EF\u5BFC\u5165\u7684\u7F51\u9875\u4E66\u7B7E",
            "dock.add.bookmarksLoadFailed": "\u8BFB\u53D6\u4E66\u7B7E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.add.previewAria": "\u56FE\u6807\u5B9E\u65F6\u9884\u89C8",
            "dock.add.previewCaption": "\u5B9E\u65F6\u9884\u89C8",
            "dock.add.previewTitle": "\u7F51\u7AD9\u6807\u9898",
            "dock.add.previewHint": "\u81EA\u52A8\u56FE\u6807\u4E0D\u53EF\u7528\u65F6\uFF0C\u4F1A\u4F9D\u6B21\u964D\u7EA7\u5230\u6B21\u6E05\u56FE\u6807\u4E0E\u6587\u5B57\u3002",
            "dock.add.cancel": "\u53D6\u6D88",
            "dock.add.save": "\u4FDD\u5B58\u5230\u62D3\u5C55\u575E",
            "dock.add.import": "\u5BFC\u5165\u5230\u62D3\u5C55\u575E",
            "dock.add.saving": "\u6B63\u5728\u4FDD\u5B58\u2026",
            "dock.add.currentPage": "\u5F53\u524D\u7F51\u9875",
            "dock.add.untitledBookmark": "\u672A\u547D\u540D\u4E66\u7B7E",
            "dock.add.otherBookmarks": "\u5176\u4ED6\u4E66\u7B7E",
            "dock.add.selectBookmark": "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4E66\u7B7E",
            "dock.add.allDuplicates": "\u6240\u9009\u7684 {count} \u4E2A\u7F51\u7AD9\u5747\u5DF2\u5728\u7F51\u9875\u62D3\u5C55\u575E\u4E2D",
            "dock.add.importFailed": "\u5BFC\u5165\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.add.importPartial": "\u5DF2\u5BFC\u5165 {count} \u4E2A\uFF0C\u968F\u540E\u5931\u8D25\uFF1A{reason}",
            "dock.add.imported": "\u5DF2\u5BFC\u5165 {count} \u4E2A\u4E66\u7B7E{suffix}",
            "dock.add.skipped": "\uFF0C\u8DF3\u8FC7 {count} \u4E2A\u5DF2\u56FA\u5B9A\u7F51\u7AD9",
            "dock.add.titleRequired": "\u8BF7\u8F93\u5165\u7F51\u7AD9\u6807\u9898",
            "dock.add.urlInvalid": "\u7F51\u9875\u7F51\u5740\u65E0\u6548",
            "dock.add.textIconInvalid": "\u6587\u5B57\u56FE\u6807\u4EC5\u652F\u6301 1\uFF5E10 \u4E2A\u4E2D\u82F1\u6587\u5B57\u6BCD",
            "dock.add.saveFailed": "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
            "dock.add.saveFailedWithReason": "\u4FDD\u5B58\u5931\u8D25\uFF1A{reason}",
            "dock.add.imageRequired": "\u8BF7\u5148\u9009\u62E9\u4E00\u5F20\u672C\u5730\u56FE\u7247",
            "dock.add.updated": "\u7F51\u9875\u5DF2\u66F4\u65B0",
            "dock.add.added": "\u7F51\u9875\u5DF2\u6DFB\u52A0",
            "dock.add.imageReadingFailed": "\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.add.imageUnrecognized": "\u65E0\u6CD5\u8BC6\u522B\u8FD9\u5F20\u56FE\u7247",
            "dock.add.imageTypeInvalid": "\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u6216 SVG \u56FE\u7247",
            "dock.add.imageTooLarge": "\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 512KB",
            "dock.add.imageSizeInvalid": "\u56FE\u7247\u5C3A\u5BF8\u65E0\u6548",
            "dock.add.imageUnavailable": "\u5F53\u524D\u9875\u9762\u65E0\u6CD5\u5904\u7406\u56FE\u7247",
            "dock.add.imageCompressedTooLarge": "\u56FE\u7247\u538B\u7F29\u540E\u4ECD\u7136\u8FC7\u5927\uFF0C\u8BF7\u6362\u4E00\u5F20\u66F4\u7B80\u6D01\u7684\u56FE\u7247",
            "dock.add.imageProcessing": "\u6B63\u5728\u5904\u7406\u56FE\u7247\u2026",
            "dock.add.imageUploadHint": "PNG / JPEG / WebP / SVG\uFF0C\u4E0D\u8D85\u8FC7 512KB",
            "dock.add.imageProcessFailed": "\u56FE\u7247\u5904\u7406\u5931\u8D25",
            "dock.search.engineAria": "\u4F7F\u7528 {engine} \u641C\u7D22",
            "dock.search.source.common": "\u5E38\u7528",
            "dock.search.source.fixed": "\u56FA\u5B9A",
            "dock.search.source.tab": "\u9875\u7B7E",
            "dock.search.source.bookmark": "\u4E66\u7B7E",
            "dock.search.source.history": "\u5386\u53F2",
            "dock.search.source.web": "\u7F51\u9875",
            "dock.search.source.unknown": "\u5176\u4ED6",
            "dock.search.recents": "\u6700\u8FD1\u641C\u7D22",
            "dock.search.resultCount": "{count} \u6761",
            "dock.search.noRecents": "\u6682\u65E0\u6700\u8FD1\u641C\u7D22\uFF0C\u8F93\u5165\u5173\u952E\u8BCD\u5F00\u59CB\u641C\u7D22",
            "dock.search.results": "\u76F8\u5173\u7ED3\u679C",
            "dock.search.sources": "\u9875\u7B7E \xB7 \u4E66\u7B7E \xB7 \u6D4F\u89C8\u5386\u53F2",
            "dock.search.emptyPermission": "\u672A\u627E\u5230\u5339\u914D\u7684\u53EF\u7528\u7ED3\u679C\uFF1B\u6388\u6743\u540E\u5C06\u81EA\u52A8\u8865\u5145\u5BF9\u5E94\u7ED3\u679C\u3002",
            "dock.search.empty": "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\uFF1B\u6309 Enter \u4F7F\u7528\u5F53\u524D\u641C\u7D22\u5F15\u64CE\u641C\u7D22",
            "dock.search.openPageFailed": "\u6253\u5F00\u641C\u7D22\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.search.openResultFailed": "\u6253\u5F00\u641C\u7D22\u7ED3\u679C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "dock.search.title": "\u7F51\u9875\u641C\u7D22",
            "dock.search.description": "\u5FEB\u901F\u641C\u7D22\u5F53\u524D\u9875\u7B7E\u3001\u4E66\u7B7E\u4E0E\u6D4F\u89C8\u5386\u53F2",
            "dock.search.close": "\u5173\u95ED\u7F51\u9875\u641C\u7D22",
            "dock.search.placeholder": "\u641C\u7D22\u7F51\u9875\u3001\u9875\u7B7E\u6216\u4E66\u7B7E",
            "dock.search.clear": "\u6E05\u7A7A\u641C\u7D22\u5185\u5BB9",
            "dock.search.chooseEngine": "\u9009\u62E9\u641C\u7D22\u5F15\u64CE",
            "siteEditor.category": "\u5206\u7C7B",
            "siteEditor.title": "\u7F51\u7AD9\u6807\u9898",
            "siteEditor.url": "\u7F51\u9875\u5730\u5740",
            "siteEditor.icon": "\u7F51\u7AD9\u56FE\u6807",
            "siteEditor.iconSource": "\u56FE\u6807\u6765\u6E90",
            "siteEditor.iconAuto": "\u81EA\u52A8\u83B7\u53D6",
            "siteEditor.iconText": "\u6587\u5B57\u56FE\u6807",
            "siteEditor.iconUpload": "\u672C\u5730\u4E0A\u4F20",
            "siteEditor.iconTextLabel": "\u56FE\u6807\u6587\u5B57",
            "siteEditor.iconTextPlaceholder": "\u6700\u591A 10 \u4E2A\u5B57\u7B26",
            "siteEditor.iconColor": "\u6587\u5B57\u56FE\u6807\u989C\u8272",
            "siteEditor.uploadChoose": "\u9009\u62E9\u672C\u5730\u56FE\u7247",
            "siteEditor.uploadHint": "PNG / JPEG / WebP / SVG\uFF0C\u4E0D\u8D85\u8FC7 512 KB",
            "tabList.untitled": "\uFF08\u65E0\u6807\u9898\uFF09",
            "tabList.unnamedGroup": "\u672A\u547D\u540D\u5206\u7EC4",
            "tabList.dockUnpin": "\u4ECE\u7F51\u9875\u62D3\u5C55\u575E\u53D6\u6D88\u56FA\u5B9A",
            "tabList.dockPin": "\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            "tabList.previewAlt": "{title} \u9884\u89C8",
            "tabList.previewLoading": "\u6B63\u5728\u52A0\u8F7D\u9884\u89C8\u2026",
            "tabList.pinned": "\u56FA\u5B9A",
            "tabList.close": "\u5173\u95ED\u9875\u7B7E",
            "tabList.switch": "\u5207\u6362",
            "tabList.duplicate": "\u590D\u5236",
            "tabList.select": "\u9009\u62E9",
            "tabList.unselect": "\u53D6\u6D88\u9009\u62E9",
            "tabList.unpin": "\u53D6\u6D88\u56FA\u5B9A",
            "tabList.pin": "\u56FA\u5B9A",
            "tabList.removeGroup": "\u79FB\u51FA\u5206\u7EC4",
            "tabList.addGroup": "\u52A0\u5165\u5206\u7EC4",
            "tabList.currentWindow": "\u5F53\u524D\u7A97\u53E3",
            "tabList.active": "\u5F53\u524D\u6807\u7B7E",
            "tabList.window": "\u7A97\u53E3 {number}{state}",
            "tabList.window.minimized": " [\u6700\u5C0F\u5316]",
            "tabList.window.maximized": " [\u6700\u5927\u5316]",
            "tabList.window.fullscreen": " [\u5168\u5C4F]",
            "tabList.editGroup": "\u70B9\u51FB\u7F16\u8F91\u5206\u7EC4",
            "tabList.edit": "\u7F16\u8F91",
            "tabList.dissolveGroup": "\u89E3\u6563\u5206\u7EC4",
            "tabList.group": "\u5206\u7EC4",
            "tabList.tab": "\u9875\u7B7E",
            "tabList.tabCount": "{count} \u4E2A\u9875\u7B7E",
            "tabList.restore": "\u6062\u590D",
            "tabList.delete": "\u5220\u9664",
            "tabList.groupNamePlaceholder": "\u5206\u7EC4\u540D\u79F0\uFF08\u53EF\u9009\uFF09",
            "tabList.cancel": "\u53D6\u6D88",
            "tabList.searchEmpty": '\u672A\u627E\u5230\u4E0E "{query}" \u76F8\u5173\u7684\u6807\u7B7E',
            "tabList.searchHint": "\u8BD5\u8BD5\u641C\u7D22\u57DF\u540D\u3001\u9875\u9762\u6807\u9898\u6216\u5206\u7EC4\u540D\u79F0",
            "tabList.empty": "\u6682\u65F6\u8FD8\u6CA1\u6709\u53EF\u7BA1\u7406\u7684\u9875\u7B7E",
            "tabList.emptyHint": "\u6253\u5F00\u66F4\u591A\u9875\u9762\uFF0C\u6216\u7A0D\u540E\u518D\u8BD5",
            "tabList.pinnedSection": "\u5DF2\u56FA\u5B9A ({count})",
            "tabList.ungroupedSection": "\u672A\u5206\u7EC4 ({count})",
            "tabList.expandMore": "\u5C55\u5F00\u5176\u4F59 {count} \u4E2A\u6807\u7B7E",
            "tabList.collapse": "\u6536\u8D77",
            "tabList.recycle": "\u56DE\u6536\u7AD9 ({count})",
            "tabList.clear": "\u6E05\u7A7A",
            "tabList.back": "\u8FD4\u56DE\u9875\u7B7E",
            "tabList.recycleEmpty": "\u56DE\u6536\u7AD9\u4E3A\u7A7A",
            "tabList.recycleHint": "\u5173\u95ED\u7684\u9875\u7B7E\u548C\u5206\u7EC4\u4F1A\u6682\u5B58\u5728\u8FD9\u91CC\uFF0C\u53EF\u968F\u65F6\u6062\u590D",
            "tabList.selected": "\u5DF2\u9009 {count}",
            "permission.bookmarks.required": "\u9700\u8981\u4E66\u7B7E\u6743\u9650\u624D\u80FD\u4ECE\u4E66\u7B7E\u5BFC\u5165\uFF1B\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002",
            "permission.bookmarks.allow": "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E",
            "permission.bookmarks.denied": "\u672A\u83B7\u5F97\u4E66\u7B7E\u6743\u9650\uFF1B\u672A\u5BFC\u5165\u4EFB\u4F55\u4E66\u7B7E\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002",
            "permission.bookmarks.unsupported": "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u6309\u9700\u7533\u8BF7\u4E66\u7B7E\u6743\u9650\u3002",
            "permission.history.required": "\u9700\u8981\u5386\u53F2\u8BB0\u5F55\u6743\u9650\u624D\u80FD\u663E\u793A\u5386\u53F2\u7ED3\u679C\uFF1B\u5F53\u524D\u9875\u7B7E\u641C\u7D22\u4ECD\u53EF\u7528\u3002",
            "permission.history.allow": "\u5141\u8BB8\u8BFB\u53D6\u6D4F\u89C8\u5668\u5386\u53F2",
            "permission.history.denied": "\u672A\u83B7\u5F97\u5386\u53F2\u8BB0\u5F55\u6743\u9650\uFF1B\u4ECD\u53EF\u641C\u7D22\u5F53\u524D\u9875\u7B7E\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002",
            "permission.history.unsupported": "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u6309\u9700\u7533\u8BF7\u5386\u53F2\u8BB0\u5F55\u6743\u9650\u3002",
            "ai.settings.nav": "AI \u667A\u80FD\u6574\u7406",
            "ai.adjustment.summary": "\u672C\u8F6E AI \u8C03\u6574 {count} \u9879",
            "ai.adjustment.view": "\u67E5\u770B",
            "ai.adjustment.hide": "\u6536\u8D77",
            "ai.adjustment.undo": "\u64A4\u9500",
            "ai.adjustment.undoing": "\u6B63\u5728\u64A4\u9500",
            "ai.adjustment.undone": "\u5DF2\u64A4\u9500",
            "ai.adjustment.close": "\u5173\u95ED\u8BE6\u60C5",
            "ai.adjustment.panelTitle": "AI \u8C03\u6574\u8BE6\u60C5",
            "ai.adjustment.before": "\u8C03\u6574\u524D",
            "ai.adjustment.after": "\u8C03\u6574\u540E",
            "ai.adjustment.reason": "\u539F\u56E0",
            "ai.adjustment.create_ai_task": "\u521B\u5EFA\u4EFB\u52A1",
            "ai.adjustment.rename_ai_task": "\u4EFB\u52A1\u6539\u540D",
            "ai.adjustment.assign_ai_relation": "\u9875\u9762\u5F52\u7C7B",
            "ai.adjustment.reorder_ai_tasks": "\u4EFB\u52A1\u6392\u5E8F",
            "ai.adjustment.conflict": "\u65E0\u6CD5\u64A4\u9500\uFF1A\u4F60\u5DF2\u5728 AI \u8C03\u6574\u540E\u624B\u52A8\u4FEE\u6539\u76F8\u5173\u5185\u5BB9\u3002",
            "ai.adjustment.failed": "\u64A4\u9500\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "ai.adjustment.empty": "\u6CA1\u6709\u53EF\u663E\u793A\u7684 AI \u8C03\u6574\u3002",
            "ai.settings.title": "AI \u667A\u80FD\u6574\u7406",
            "ai.settings.description": "\u914D\u7F6E\u865A\u62DF\u4EFB\u52A1\u6574\u7406\u3001\u6A21\u578B\u6E20\u9053\u548C\u672C\u673A\u5BC6\u94A5",
            "ai.settings.scopeTitle": "AI \u53EA\u6574\u7406\u865A\u62DF\u4EFB\u52A1",
            "ai.settings.scope": "AI \u53EF\u4EE5\u81EA\u52A8\u4FEE\u6539\u53F3\u4FA7\u865A\u62DF\u4EFB\u52A1\u5173\u7CFB\u548C AI \u7BA1\u7406\u7684\u4EFB\u52A1\uFF0C\u4F46\u7EDD\u4E0D\u6253\u5F00\u3001\u5173\u95ED\u3001\u79FB\u52A8\u3001\u5206\u7EC4\u6216\u91CD\u6392\u771F\u5B9E\u9875\u7B7E\u3002",
            "ai.settings.enabled": "\u542F\u7528 AI \u667A\u80FD\u6574\u7406",
            "ai.settings.enabledHint": "\u5173\u95ED\u65F6\u4E0D\u4F1A\u4EA7\u751F\u4E91\u7AEF\u8BF7\u6C42\uFF0C\u672C\u5730\u5EFA\u8BAE\u4ECD\u53EF\u4F7F\u7528",
            "ai.settings.autoOrganize": "\u5141\u8BB8\u9AD8\u7F6E\u4FE1\u7ED3\u679C\u81EA\u52A8\u6574\u7406",
            "ai.settings.autoOrganizeHint": "\u53EA\u5904\u7406\u901A\u8FC7\u672C\u5730\u5B89\u5168\u95E8\u69DB\u7684\u9AD8\u7F6E\u4FE1\u865A\u62DF\u4EFB\u52A1\u53D8\u5316",
            "ai.settings.channelTitle": "\u6A21\u578B\u6E20\u9053",
            "ai.settings.channelHint": "OpenAI \u517C\u5BB9\u63A5\u53E3\uFF1B\u5730\u5740\u548C\u6A21\u578B\u53EF\u4EE5\u4FEE\u6539",
            "ai.settings.provider": "OpenAI \u517C\u5BB9",
            "ai.settings.baseUrl": "API \u5730\u5740",
            "ai.settings.model": "\u6A21\u578B",
            "ai.settings.apiKey": "API Key",
            "ai.settings.keyPreserved": "\u5DF2\u4FDD\u5B58\u5BC6\u94A5\uFF1B\u7559\u7A7A\u5C06\u7EE7\u7EED\u4F7F\u7528",
            "ai.settings.keyPlaceholder": "\u8F93\u5165 API Key",
            "ai.settings.showKey": "\u663E\u793A\u5BC6\u94A5",
            "ai.settings.hideKey": "\u9690\u85CF\u5BC6\u94A5",
            "ai.settings.clearKey": "\u6E05\u9664\u5BC6\u94A5",
            "ai.settings.localKey": "\u5BC6\u94A5\u4EC5\u4FDD\u5B58\u5728\u672C\u673A\uFF0C\u4E0D\u540C\u6B65\u3001\u4E0D\u5BFC\u51FA\uFF0C\u4E5F\u4E0D\u4F1A\u51FA\u73B0\u5728\u65E5\u5FD7\u6216\u63D0\u793A\u6D88\u606F\u4E2D\u3002",
            "ai.settings.previewTitle": "\u67E5\u770B\u53D1\u9001\u5B57\u6BB5\u9884\u89C8",
            "ai.settings.previewHint": "\u5F00\u542F AI \u540E\uFF0C\u4EC5\u53D1\u9001\u672C\u5730\u7B5B\u9009\u51FA\u7684\u6700\u5C0F\u5019\u9009\u4FE1\u606F\u3002",
            "ai.settings.previewPage": "\u5F53\u524D\u9875\u9762",
            "ai.settings.previewPageFields": "\u8131\u654F\u6807\u9898\u3001\u6CE8\u518C\u57DF\u540D\u3001\u8DEF\u5F84\u7C7B\u578B",
            "ai.settings.previewTasks": "\u5019\u9009\u4EFB\u52A1",
            "ai.settings.previewTaskFields": "\u4EFB\u52A1\u540D\u79F0\u3001\u9886\u57DF\u3001\u5C11\u91CF\u8131\u654F\u6838\u5FC3\u9875\u6807\u9898",
            "ai.settings.previewSignals": "\u672C\u5730\u4FE1\u53F7",
            "ai.settings.previewSignalFields": "\u805A\u5408\u540E\u7684\u8BC1\u636E\u4EE3\u7801\u3001\u98CE\u9669\u4EE3\u7801\u548C\u6B21\u6570",
            "ai.settings.previewNever": "\u4E0D\u4F1A\u53D1\u9001\u5B8C\u6574 URL\u3001\u67E5\u8BE2\u53C2\u6570\u3001\u9875\u9762\u6B63\u6587\u3001\u8F93\u5165\u5185\u5BB9\u3001Cookie\u3001\u622A\u56FE\u3001\u539F\u59CB\u884C\u4E3A\u8F68\u8FF9\u6216 API Key\u3002",
            "ai.settings.actionHint": "\u4FEE\u6539\u540E\u5148\u6D4B\u8BD5\u8FDE\u63A5\uFF0C\u518D\u4FDD\u5B58\u8BBE\u7F6E",
            "ai.settings.testConnection": "\u6D4B\u8BD5\u8FDE\u63A5",
            "ai.settings.save": "\u4FDD\u5B58 AI \u8BBE\u7F6E",
            "ai.settings.loading": "\u6B63\u5728\u8BFB\u53D6\u672C\u673A\u8BBE\u7F6E\u2026",
            "ai.settings.saving": "\u6B63\u5728\u4FDD\u5B58\u672C\u673A\u8BBE\u7F6E\u2026",
            "ai.settings.testing": "\u6B63\u5728\u6D4B\u8BD5\u8FDE\u63A5\u2026",
            "ai.settings.saved": "AI \u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u5728\u672C\u673A\u3002",
            "ai.settings.savedPermissionPending": "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u8BE5\u5730\u5740\u6743\u9650\u5C1A\u672A\u6388\u6743\uFF0C\u8FDE\u63A5\u6D4B\u8BD5\u524D\u53EF\u518D\u6B21\u5141\u8BB8\u3002",
            "ai.settings.testSuccess": "\u8FDE\u63A5\u6210\u529F\uFF0C\u53EF\u4EE5\u4F7F\u7528\u5F53\u524D\u6E20\u9053\u3002",
            "ai.settings.keyWillClear": "\u4FDD\u5B58\u540E\u5C06\u6E05\u9664\u672C\u673A\u5BC6\u94A5\u3002",
            "ai.settings.error.permission": "\u6CA1\u6709\u83B7\u5F97\u8BE5\u5730\u5740\u7684\u8BBF\u95EE\u6743\u9650\uFF1B\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002",
            "ai.settings.error.unauthorized": "\u5BC6\u94A5\u88AB\u6E20\u9053\u62D2\u7EDD\uFF0C\u8BF7\u68C0\u67E5\u6216\u66F4\u6362\u5BC6\u94A5\u3002",
            "ai.settings.error.paymentRequired": "\u6E20\u9053\u4F59\u989D\u6216\u914D\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6E20\u9053\u8D26\u6237\u3002",
            "ai.settings.error.forbidden": "\u6E20\u9053\u62D2\u7EDD\u8BBF\u95EE\u5F53\u524D\u63A5\u53E3\u6216\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5\u8D26\u6237\u548C\u6A21\u578B\u6743\u9650\u3002",
            "ai.settings.error.quotaOrAccess": "\u6E20\u9053\u4F59\u989D\u4E0D\u8DB3\u6216\u6A21\u578B\u6743\u9650\u672A\u5F00\u901A\uFF0C\u8BF7\u68C0\u67E5\u6E20\u9053\u8D26\u6237\u3002",
            "ai.settings.error.upstreamReason": "\u6E20\u9053\u539F\u56E0\uFF1A",
            "ai.settings.error.badRequest": "\u6E20\u9053\u4E0D\u63A5\u53D7\u5F53\u524D\u6A21\u578B\u6216\u8BF7\u6C42\u53C2\u6570\uFF0C\u8BF7\u6838\u5BF9\u6A21\u578B\u540D\u79F0\u3002",
            "ai.settings.error.notFound": "API \u5730\u5740\u6216\u6A21\u578B\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u6A21\u578B\u540D\u79F0\u3002",
            "ai.settings.error.rateLimit": "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "ai.settings.error.server": "\u6E20\u9053\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002",
            "ai.settings.error.timeout": "\u8FDE\u63A5\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u6216\u7F51\u7EDC\u3002",
            "ai.settings.error.network": "\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u7F51\u7EDC\u3002",
            "ai.settings.error.baseUrl": "API \u5730\u5740\u5FC5\u987B\u662F\u6709\u6548\u7684 HTTPS \u5730\u5740\u3002",
            "ai.settings.error.invalidSettings": "AI \u6E20\u9053\u8BBE\u7F6E\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u3001\u6A21\u578B\u548C\u5BC6\u94A5\u3002",
            "ai.settings.error.invalidResponse": "\u6E20\u9053\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BFB\u53D6\u6216\u4E0D\u7B26\u5408\u63A5\u53E3\u683C\u5F0F\u7684\u54CD\u5E94\u3002",
            "ai.settings.error.provider": "\u6E20\u9053\u62D2\u7EDD\u4E86\u8BF7\u6C42\uFF0C\u4F46\u6CA1\u6709\u8FD4\u56DE\u53EF\u8BC6\u522B\u7684\u9519\u8BEF\u7C7B\u578B\u3002",
            "ai.settings.error.keyRequired": "\u8BF7\u586B\u5199 API Key\uFF0C\u6216\u4FDD\u7559\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5\u3002",
            "ai.settings.error.model": "\u8BF7\u586B\u5199\u6A21\u578B\u540D\u79F0\u3002",
            "ai.settings.error.load": "\u8BFB\u53D6 AI \u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "ai.settings.error.save": "\u4FDD\u5B58 AI \u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
            "ai.settings.error.test": "\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u7F6E\u540E\u91CD\u8BD5\u3002",
            "task.newPrompt": "\u8F93\u5165\u4EFB\u52A1\u540D\u79F0",
            "task.renameHint": "\u70B9\u51FB\u53EF\u4FEE\u6539\u6807\u9898",
            "task.renameAria": "\u4FEE\u6539\u4EFB\u52A1\u6807\u9898\uFF1A{title}",
            "task.expandPages": "\u5C55\u5F00\u5168\u90E8\u9875\u7B7E",
            "task.collapsePages": "\u6536\u8D77\u4E3A\u4E00\u884C",
            "task.undoLastChange": "\u64A4\u9500\u4E0A\u4E00\u6B21\u4EFB\u52A1\u4FEE\u6539",
            "task.dragToReorder": "\u62D6\u52A8\u8C03\u6574\u4EFB\u52A1\u987A\u5E8F",
            "task.dragDisabledByFilter": "\u5207\u6362\u4E3A\u624B\u52A8\u6392\u5E8F\u5E76\u6E05\u9664\u7B5B\u9009\u540E\u53EF\u62D6\u52A8",
            "task.moveUp": "\u4E0A\u79FB\u4EFB\u52A1",
            "task.moveDown": "\u4E0B\u79FB\u4EFB\u52A1",
            "task.openAll": "\u6253\u5F00\u5168\u90E8",
            "task.closeAll": "\u5173\u95ED\u5168\u90E8",
            "task.more": "\u66F4\u591A",
            "task.dissolve": "\u89E3\u6563\u4EFB\u52A1",
            "task.removePage": "\u79FB\u51FA\u4EFB\u52A1",
            "task.source.manual": "\u624B\u52A8\u5206\u7C7B",
            "task.source.smart": "\u667A\u80FD\u5206\u7C7B",
            "suggestion.source.local": "\u672C\u5730\u5EFA\u8BAE",
            "suggestion.source.ai": "AI \u5EFA\u8BAE",
            "suggestion.confidence.high": "\u628A\u63E1\u8F83\u9AD8",
            "suggestion.confidence.medium": "\u628A\u63E1\u4E2D\u7B49",
            "suggestion.reason.opener_link": "\u7531\u8BE5\u4EFB\u52A1\u4E2D\u7684\u9875\u9762\u6253\u5F00",
            "suggestion.reason.stable_object": "\u4E0E\u8BE5\u4EFB\u52A1\u4F7F\u7528\u540C\u4E00\u5DE5\u4F5C\u5BF9\u8C61",
            "suggestion.reason.qualified_return": "\u66FE\u591A\u6B21\u5728\u8BE5\u9875\u9762\u4E0E\u4EFB\u52A1\u9875\u9762\u95F4\u5207\u6362",
            "suggestion.reason.task_snapshot_restore": "\u4E0E\u8BE5\u4EFB\u52A1\u73B0\u573A\u4E00\u540C\u6062\u590D",
            "suggestion.reason.native_group_restore": "\u4E0E\u8BE5\u4EFB\u52A1\u9875\u9762\u4ECE\u540C\u4E00\u6807\u7B7E\u7EC4\u6062\u590D",
            "suggestion.reason.title_overlap": "\u9875\u9762\u6807\u9898\u4E0E\u8BE5\u4EFB\u52A1\u76F8\u5173",
            "suggestion.reason.same_native_group": "\u4E0E\u8BE5\u4EFB\u52A1\u9875\u9762\u4F4D\u4E8E\u540C\u4E00\u6807\u7B7E\u7EC4",
            "suggestion.reason.same_window": "\u4E0E\u8BE5\u4EFB\u52A1\u9875\u9762\u4F4D\u4E8E\u540C\u4E00\u7A97\u53E3",
            "suggestion.reason.same_domain": "\u4E0E\u8BE5\u4EFB\u52A1\u9875\u9762\u6765\u81EA\u540C\u4E00\u7F51\u7AD9",
            "suggestion.reason.risk_suppressed": "\u5B58\u5728\u4E0D\u786E\u5B9A\u56E0\u7D20\uFF0C\u5DF2\u964D\u4F4E\u628A\u63E1",
            "suggestion.reason.ambiguous": "\u53E6\u6709\u76F8\u8FD1\u5019\u9009\uFF0C\u8BF7\u9009\u62E9",
            "suggestion.action.addToTask": "\u52A0\u5165\u4EFB\u52A1",
            "suggestion.action.alsoLink": "\u540C\u65F6\u5173\u8054",
            "suggestion.action.reject": "\u62D2\u7EDD",
            "suggestion.action.undo": "\u64A4\u9500",
            "suggestion.batch.enter": "\u6279\u91CF\u5904\u7406",
            "suggestion.batch.select": "\u9009\u62E9\u6B64\u5EFA\u8BAE",
            "suggestion.batch.selectAll": "\u5168\u9009\u5F53\u524D\u5EFA\u8BAE",
            "suggestion.batch.count": "\u5DF2\u9009 {count} \u6761",
            "suggestion.batch.accept": "\u6279\u91CF\u52A0\u5165\u4EFB\u52A1",
            "suggestion.batch.reject": "\u6279\u91CF\u62D2\u7EDD",
            "suggestion.batch.cancel": "\u53D6\u6D88\u6279\u91CF\u5904\u7406",
            "suggestion.batch.running": "\u6B63\u5728\u5904\u7406 {count} \u6761\u5EFA\u8BAE",
            "suggestion.batch.limit": "\u4E00\u6B21\u6700\u591A\u9009\u62E9 {count} \u6761\u5EFA\u8BAE",
            "suggestion.label.addToTask": "\u5EFA\u8BAE\u52A0\u5165\u300C{task}\u300D",
            "suggestion.label.chooseTask": "\u9009\u62E9\u6700\u7B26\u5408\u5F53\u524D\u610F\u56FE\u7684\u4EFB\u52A1",
            "suggestion.label.choosePrimary": "\u9009\u62E9\u8FD9\u4E2A\u9875\u9762\u7684\u4E3B\u8981\u4EFB\u52A1",
            "suggestion.label.multiAssociate": "\u53EF\u7EE7\u7EED\u540C\u65F6\u5173\u8054\u4EE5\u4E0B\u4EFB\u52A1",
            "suggestion.label.relatedPage": "\u9875\u9762\u201C{page}\u201D\u8FD8\u6709\u5173\u8054\u4EFB\u52A1\u5019\u9009",
            "suggestion.notice.rejected": "\u5DF2\u62D2\u7EDD\u5EFA\u8BAE",
            "suggestion.notice.accepted": "\u5DF2\u52A0\u5165\u4EFB\u52A1",
            "suggestion.state.loading": "\u6B63\u5728\u5206\u6790",
            "suggestion.aria.chooseTask": "\u9009\u62E9\u4EFB\u52A1\uFF1A{task}",
            "suggestion.aria.undoAccepted": "\u64A4\u9500\u667A\u80FD\u5F52\u7C7B\uFF1A{task}",
            "suggestion.error.expired": "\u5EFA\u8BAE\u5DF2\u8FC7\u671F",
            "suggestion.error.conflict": "\u64CD\u4F5C\u51B2\u7A81",
            "suggestion.error.failed": "\u5EFA\u8BAE\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "suggestion.why": "\u4E3A\u4EC0\u4E48\u8FD9\u6837\u5206",
            "toast.suggestionAccepted": "\u5DF2\u52A0\u5165\u4EFB\u52A1",
            "toast.suggestionRejected": "\u5DF2\u62D2\u7EDD\u5EFA\u8BAE",
            "toast.suggestionUndone": "\u5DF2\u64A4\u9500\u5EFA\u8BAE\u64CD\u4F5C",
            "toast.suggestionBatchSuccess": "\u5DF2\u6210\u529F\u5904\u7406 {count} \u6761\u5EFA\u8BAE",
            "toast.suggestionBatchPartial": "\u6210\u529F {succeeded} \u6761\uFF0C\u5931\u8D25 {failed} \u6761",
            "task.pageMoveUp": "\u4E0A\u79FB\u9875\u9762",
            "task.pageMoveDown": "\u4E0B\u79FB\u9875\u9762",
            "task.chooseNewPrimary": "\u9009\u62E9\u65B0\u7684\u4E3B\u8981\u4EFB\u52A1",
            "task.relation.primary": "\u4E3B\u8981\u4EFB\u52A1",
            "task.relation.primaryHint": "\u6B64\u9875\u9762\u7684\u4E3B\u8981\u4EFB\u52A1",
            "task.relation.assign": "\u52A0\u5165\u4EFB\u52A1\u2026",
            "task.relation.move": "\u79FB\u52A8\u5230\u4EFB\u52A1\u2026",
            "task.relation.share": "\u540C\u65F6\u5173\u8054\u4EFB\u52A1\u2026",
            "task.relation.makePrimary": "\u8BBE\u4E3A\u4E3B\u8981\u4EFB\u52A1",
            "task.relation.assignHint": "\u9009\u62E9\u9875\u9762\u7684\u4E3B\u8981\u4EFB\u52A1\u3002",
            "task.relation.moveHint": "\u76EE\u6807\u5C06\u6210\u4E3A\u4E3B\u8981\u4EFB\u52A1\uFF1B\u5F53\u524D\u4EFB\u52A1\u5173\u7CFB\u4F1A\u79FB\u9664\uFF0C\u771F\u5B9E\u9875\u7B7E\u4E0D\u53D7\u5F71\u54CD\u3002",
            "task.relation.shareHint": "\u4FDD\u7559\u5F53\u524D\u4E3B\u8981\u4EFB\u52A1\uFF0C\u5E76\u65B0\u589E\u4E00\u4E2A\u5173\u8054\u4EFB\u52A1\u3002",
            "task.relation.dropChoice": "\u9009\u62E9\u5982\u4F55\u52A0\u5165\u201C{task}\u201D",
            "task.relation.moveHere": "\u79FB\u52A8\u5230\u6B64\u4EFB\u52A1",
            "task.relation.shareHere": "\u540C\u65F6\u5173\u8054\u6B64\u4EFB\u52A1",
            "common.back": "\u8FD4\u56DE",
            "task.confirmDissolve": "\u786E\u5B9A\u89E3\u6563\u8FD9\u4E2A\u4EFB\u52A1\u5417\uFF1F\u771F\u5B9E\u9875\u7B7E\u4E0D\u4F1A\u5173\u95ED\uFF0C\u4EFB\u52A1\u7EC4\u53EF\u4EE5\u4ECE\u56DE\u6536\u7AD9\u6062\u590D\u3002",
            "task.confirmDeleteTrash": "\u786E\u5B9A\u6C38\u4E45\u5220\u9664\u8FD9\u4E2A\u56DE\u6536\u9879\u5417\uFF1F\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002",
            "toast.taskAssigned": "\u5DF2\u624B\u52A8\u52A0\u5165\u4EFB\u52A1",
            "toast.taskRelationMoved": "\u5DF2\u79FB\u52A8\u4EFB\u52A1\u5173\u7CFB\uFF0C\u771F\u5B9E\u9875\u7B7E\u672A\u79FB\u52A8",
            "toast.taskRelationShared": "\u5DF2\u540C\u65F6\u5173\u8054\u4EFB\u52A1",
            "toast.taskPrimaryChanged": "\u4E3B\u8981\u4EFB\u52A1\u5DF2\u66F4\u65B0",
            "toast.taskRemoved": "\u5DF2\u4ECE\u4EFB\u52A1\u79FB\u51FA\uFF0C\u771F\u5B9E\u9875\u7B7E\u4E0D\u4F1A\u5173\u95ED",
            "toast.taskStatusChanged": "\u4EFB\u52A1\u72B6\u6001\u5DF2\u66F4\u65B0",
            "toast.taskDomainChanged": "\u4EFB\u52A1\u9886\u57DF\u5DF2\u66F4\u65B0",
            "toast.taskReordered": "\u4EFB\u52A1\u987A\u5E8F\u5DF2\u66F4\u65B0",
            "toast.taskPagesReordered": "\u4EFB\u52A1\u9875\u9762\u987A\u5E8F\u5DF2\u66F4\u65B0",
            "toast.taskCreated": "\u4EFB\u52A1\u5DF2\u521B\u5EFA",
            "toast.taskRenamed": "\u4EFB\u52A1\u5DF2\u66F4\u65B0",
            "toast.taskMissing": "\u8FD9\u4E2A\u4EFB\u52A1\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5173\u95ED\u5F39\u7A97\u540E\u91CD\u8BD5",
            "toast.taskDissolved": "\u4EFB\u52A1\u5DF2\u89E3\u6563\uFF0C\u53EF\u4ECE\u56DE\u6536\u7AD9\u6062\u590D\uFF1B\u771F\u5B9E\u9875\u7B7E\u4E0D\u4F1A\u5173\u95ED",
            "toast.taskRestored": "\u4EFB\u52A1\u7EC4\u5DF2\u6062\u590D",
            "toast.trashRestored": "\u56DE\u6536\u9879\u5DF2\u6062\u590D",
            "toast.trashRestoreAll": "\u56DE\u6536\u7AD9\u5185\u5BB9\u5DF2\u6062\u590D",
            "toast.trashDeleted": "\u56DE\u6536\u9879\u5DF2\u6C38\u4E45\u5220\u9664",
            "toast.trashLoadFailed": "\u56DE\u6536\u7AD9\u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "toast.trashActionFailed": "\u56DE\u6536\u7AD9\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "toast.taskOpened": "\u5DF2\u6253\u5F00\u4EFB\u52A1\u9875\u9762",
            "toast.taskAlreadyOpen": "\u4EFB\u52A1\u9875\u9762\u5747\u5DF2\u6253\u5F00",
            "toast.taskNoOpenTabs": "\u5F53\u524D\u6CA1\u6709\u53EF\u5173\u95ED\u7684\u4EFB\u52A1\u9875\u7B7E",
            "toast.taskTabsClosed": "\u5DF2\u5173\u95ED\u4EFB\u52A1\u4E2D\u7684\u771F\u5B9E\u9875\u7B7E",
            "toast.taskChangeFailed": "\u4EFB\u52A1\u4FEE\u6539\u5931\u8D25",
            "toast.taskChangeUndone": "\u5DF2\u64A4\u9500\u4E0A\u4E00\u6B21\u4EFB\u52A1\u4FEE\u6539",
            "toast.taskOperationPending": "\u64CD\u4F5C\u7ED3\u679C\u5C1A\u672A\u5B8C\u5168\u540C\u6B65\uFF0C\u8BF7\u7A0D\u540E\u91CD\u65B0\u6253\u5F00\u6D6E\u5C42\u68C0\u67E5",
            "toast.taskTrashCleanupPending": "\u4EFB\u52A1\u5DF2\u6062\u590D\uFF0C\u4F46\u56DE\u6536\u8BB0\u5F55\u4ECD\u5728\u6E05\u7406",
            "toast.tabPinned": "\u5DF2\u56FA\u5B9A\u8BE5\u9875\u7B7E",
            "toast.tabUnpinned": "\u5DF2\u53D6\u6D88\u56FA\u5B9A",
            "toast.tabCommandFailed": "\u9875\u7B7E\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "toast.tabNotOpen": "\u8FD9\u4E2A\u4EFB\u52A1\u9875\u9762\u5F53\u524D\u672A\u6253\u5F00",
            "toast.taskOpenFailed": "\u6253\u5F00\u4EFB\u52A1\u9875\u9762\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "toast.taskCloseFailed": "\u5173\u95ED\u4EFB\u52A1\u9875\u7B7E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            "toast.layoutSyncFailed": "\u5E03\u5C40\u8BBE\u7F6E\u6682\u65F6\u65E0\u6CD5\u540C\u6B65",
            "tab.activate": "\u5207\u6362\u5230 {title}",
            "tab.close": "\u5173\u95ED\u9875\u7B7E",
            "tab.pinned": "\u5DF2\u56FA\u5B9A",
            "tab.dockFixed": "\u5DF2\u56FA\u5B9A\u5230\u62D3\u5C55\u575E",
            "tab.more": "\u66F4\u591A\u64CD\u4F5C",
            "tab.removeFromDock": "\u4ECE\u7F51\u9875\u62D3\u5C55\u575E\u53D6\u6D88\u56FA\u5B9A",
            "tab.addToDock": "\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            "task.untitled": "\u672A\u547D\u540D\u4EFB\u52A1",
            "task.status.active": "\u8FDB\u884C\u4E2D",
            "task.status.paused": "\u5DF2\u6682\u505C",
            "task.status.done": "\u5DF2\u5B8C\u6210",
            "task.status.pending": "\u5F85\u5212\u5206",
            "domain.work": "\u5DE5\u4F5C",
            "domain.study": "\u5B66\u4E60",
            "domain.life": "\u751F\u6D3B",
            "domain.interest": "\u5174\u8DA3",
            "domain.admin": "\u4E8B\u52A1",
            "domain.other": "\u5176\u4ED6",
            "guidance.eyebrow": "\u4E3B\u52A8\u63D0\u9192",
            "guidance.close": "\u5173\u95ED\u63D0\u9192",
            "guidance.muteToday": "\u4ECA\u65E5\u4E0D\u518D\u63D0\u9192",
            "guidance.openWorkbench": "\u6309\u4F4FAlt\u952E\u6216\u70B9\u51FB\u6253\u5F00",
            "guidance.frequent.title": "\u9875\u7B7E\u5207\u5F97\u6709\u70B9\u5FEB\uFF1F\u7528 TabFlow \u4E00\u773C\u627E\u5230\u3002",
            "guidance.frequent.message": "\u7528 TabFlow \u4E00\u773C\u627E\u5230\u6B63\u5728\u5207\u6362\u7684\u9875\u9762\u3002",
            "guidance.frequent.action": "\u6309\u4F4FAlt\u952E\u6216\u70B9\u51FB\u6253\u5F00",
            "guidance.startup.title": "\u7EE7\u7EED\u4E0A\u6B21\u7684\u6D4F\u89C8\u73B0\u573A\uFF0CTabFlow \u5DF2\u5C31\u7EEA\u3002",
            "guidance.startup.message": "\u7EE7\u7EED\u4E0A\u6B21\u7684\u6D4F\u89C8\u73B0\u573A\uFF0CTabFlow \u5DF2\u5C31\u7EEA\u3002",
            "guidance.startup.action": "\u6309\u4F4FAlt\u952E\u6216\u70B9\u51FB\u6253\u5F00",
            "guidance.20.title": "\u9875\u7B7E\u591A\u8D77\u6765\u4E86\uFF0C\u7528\u4EFB\u52A1\u89C6\u56FE\u4F1A\u66F4\u6E05\u695A\u3002",
            "guidance.20.message": "\u9875\u7B7E\u591A\u8D77\u6765\u4E86\uFF0C\u7528\u4EFB\u52A1\u89C6\u56FE\u4F1A\u66F4\u6E05\u695A\u3002",
            "guidance.20.action": "\u6309\u4F4FAlt\u952E\u6216\u70B9\u51FB\u6253\u5F00",
            "guidance.30.title": "\u9875\u7B7E\u680F\u6709\u70B9\u6324\uFF0CTabFlow \u5E2E\u4F60\u5FEB\u901F\u5B9A\u4F4D\u3002",
            "guidance.30.message": "\u9875\u7B7E\u680F\u6709\u70B9\u6324\uFF0CTabFlow \u5E2E\u4F60\u5FEB\u901F\u5B9A\u4F4D\u3002",
            "guidance.30.action": "\u6309\u4F4FAlt\u952E\u6216\u70B9\u51FB\u6253\u5F00",
            taskDialogCreateTitle: "\u65B0\u589E\u4EFB\u52A1",
            taskDialogRenameTitle: "\u4FEE\u6539\u4EFB\u52A1",
            taskDialogTitleLabel: "\u4EFB\u52A1\u540D\u79F0",
            taskDialogTitlePlaceholder: "\u8F93\u5165\u4EFB\u52A1\u540D\u79F0",
            taskDialogDomainLabel: "\u9009\u62E9\u9886\u57DF",
            taskDialogCreate: "\u521B\u5EFA\u4EFB\u52A1",
            taskDialogSave: "\u4FDD\u5B58\u4FEE\u6539",
            taskDialogSaving: "\u4FDD\u5B58\u4E2D\u2026",
            taskDialogRequired: "\u8BF7\u8F93\u5165\u4EFB\u52A1\u540D\u79F0",
            taskDialogSubmitFailed: "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            taskDomain_work: "\u5DE5\u4F5C",
            taskDomain_study: "\u5B66\u4E60",
            taskDomain_life: "\u751F\u6D3B",
            taskDomain_interest: "\u5174\u8DA3",
            taskDomain_admin: "\u4E8B\u52A1",
            taskDomain_other: "\u5176\u4ED6",
            commonCancel: "\u53D6\u6D88"
          }),
          en: Object.freeze({
            "layout.split": "Split overview",
            "layout.browser": "Browser tabs",
            "layout.tasks": "Task overview",
            "pane.browser": "Current browser tabs",
            "pane.tasks": "Task overview",
            "search.placeholder": "Search title, site, or URL",
            "filter.allWindows": "All windows",
            "filter.allDomains": "All domains",
            "filter.allStatuses": "All statuses",
            "filter.all": "All",
            "locale.id": "en-US",
            "task.new": "New task",
            "task.inbox": "Inbox",
            "inbox.ignorePage": "Ignore this page",
            "inbox.markShared": "Mark as shared / multi-task",
            "inbox.excludeSite": "Do not record this site",
            "inbox.sharedBadge": "Shared / multi-task",
            "toast.inboxPreferenceFailed": "Could not save the inbox preference. Please try again.",
            "empty.tabs": "No matching tabs",
            "empty.tasks": "No tasks yet",
            "workbench.layoutLabel": "Main view",
            "workbench.layout.split": "Split overview",
            "workbench.layout.browser": "Browser tabs",
            "workbench.layout.tasks": "Task overview",
            "workbench.layout.recycle": "Recycle bin",
            "workbench.searchPlaceholder": "Search tabs, URLs, or tasks",
            "workbench.searchHint": "Task organization never reorders real tabs",
            "workbench.browserTitle": "Current browser tabs",
            "workbench.taskTitle": "Task overview",
            "workbench.inboxTitle": "Unassigned tabs",
            "workbench.inboxCandidates": "{count} candidates",
            "workbench.inboxExpand": "Show all tabs",
            "workbench.inboxCollapse": "Collapse to one row",
            "inbox.observation.observing.label": "Observing",
            "inbox.observation.observing.reason": "Waiting for more browsing activity",
            "inbox.observation.temporary_query.label": "Quick lookup",
            "inbox.observation.temporary_query.reason": "May be a short one-off lookup",
            "inbox.observation.possible_misclick.label": "Possible misclick",
            "inbox.observation.possible_misclick.reason": "Very short visit; not classified yet",
            "inbox.observation.redirect_or_ad.label": "Ad redirect",
            "inbox.observation.redirect_or_ad.reason": "May be a redirect or advertising page",
            "inbox.observation.intent_drift.label": "Intent drift",
            "inbox.observation.intent_drift.reason": "May have drifted from the current intent",
            "workbench.statusFilter": "Filter by status",
            "workbench.domainFilter": "Filter by domain",
            "workbench.taskSort": "Sort tasks",
            "workbench.taskSort.manual": "Manual order",
            "workbench.taskSort.recent": "Recently updated",
            "workbench.taskSort.title": "Name",
            "task.view.label": "Task view",
            "task.view.cards": "Cards",
            "task.view.aggregate": "Aggregate",
            "task.view.fishbone": "Fishbone",
            "task.view.experimentalBadge": "Experimental",
            "task.view.experimentalHint": "Read-only basic view based on current task relations. Full layout and interactions will be improved later.",
            "task.view.empty": "No task data to visualize",
            "task.view.emptyHint": "Create or match tasks, then return to this view.",
            "task.view.pageCount": "{count} related pages",
            "task.view.noPages": "No related pages",
            "task.view.aggregateAria": "Task aggregation view",
            "task.view.fishboneAria": "Task fishbone view",
            "workbench.allWindows": "All windows",
            "workbench.allStatuses": "All statuses",
            "workbench.allDomains": "All domains",
            "workbench.window": "Window {number}",
            "workbench.tabCount": "{count} tabs",
            "workbench.untitledTabGroup": "Untitled tab group",
            "workbench.ungroupedTabs": "Ungrouped tabs",
            "workbench.collapseTabGroup": "Collapse tab group",
            "workbench.expandTabGroup": "Expand tab group",
            "workbench.tabGroupMore": "Tab group actions",
            "workbench.activateGroupFirst": "Switch to first tab",
            "workbench.editTabGroup": "Edit name and color",
            "workbench.ungroupTabs": "Remove from group",
            "workbench.closeTabGroup": "Close tab group",
            "workbench.ungroupTabGroup": "Ungroup",
            "workbench.closeAllTabGroup": "Close all",
            "workbench.groupByDomain": "Group by domain",
            "workbench.ungroupAll": "Ungroup all",
            "privacy.title": "Privacy & data",
            "privacy.description": "Review local activity records and manage retained data",
            "settings.languageTitle": "Interface language",
            "settings.languageHint": "Follow the browser by default, or choose a language just for TabFlow.",
            "settings.languageSystem": "Follow browser",
            "settings.languageChinese": "\u4E2D\u6587",
            "settings.languageEnglish": "English",
            "settings.languageSaveFailed": "Could not save the language setting. Try again.",
            "settings.title": "Settings",
            "settings.navigation": "Settings navigation",
            "settings.activationTitle": "Permanent activation",
            "settings.activationDescription": "Unlock premium features while the core experience stays free",
            "settings.dockTitle": "Web Dock",
            "settings.dockDescription": "Manage pinned sites, appearance, and interactions",
            "settings.close": "Close settings",
            "privacy.localBehavior": "Local activity records",
            "privacy.localBehaviorHint": "Necessary tab activity stays on this device for up to 30 days and never changes tasks or real tab order automatically.",
            "privacy.behaviorClearHint": "Clearing activity history does not delete tasks, task relationships, page records, or current tab instances.",
            "privacy.dataControls": "Data controls",
            "privacy.exportHint": "Safe export includes tasks, pages, relationships, New Tab sites, and the Web Dock. It excludes API keys, activation data, screenshots, cookies, raw activity context, and full URL parameters.",
            "privacy.export": "Safe export",
            "privacy.clearBehavior": "Clear activity history",
            "privacy.clearAi": "Clear AI data",
            "privacy.clearAll": "Clear all local data",
            "privacy.clearing": "Clearing\u2026",
            "privacy.clearAiConfirm": "Clear local AI settings, API key, and runtime state? Tasks and task relationships will be kept.",
            "privacy.clearBehaviorConfirm": "Clear up to 30 days of local activity history? Tasks, relationships, page records, and current tabs will be kept.",
            "privacy.clearBehaviorSuccess": "Cleared {count} activity records. Tasks, relationships, and current tabs were kept.",
            "privacy.clearBehaviorFailed": "Could not clear activity history. Please try again.",
            "privacy.clearAllConfirm": "Clear all local TabFlow data? This deletes tasks, relationships, activity history, and the AI key and cannot be undone.",
            "privacy.clearAiSuccess": "Local AI data cleared. Tasks and task relationships were kept.",
            "privacy.clearAllSuccess": "All local TabFlow data cleared.",
            "privacy.exportSuccess": "Safe data exported.",
            "privacy.actionFailed": "Action failed. Please try again.",
            "privacy.exportFailed": "Could not export the file. Please try again.",
            "privacy.backup.title": "Backup & restore",
            "privacy.backup.hint": "Choose a TabFlow JSON backup to review a safe summary first. Nothing is overwritten before confirmation. Maximum 5 MiB.",
            "privacy.backup.reinstallWarning": "Upgrades and extension reloads preserve local data, but uninstalling normally deletes it. Export a safe backup before uninstalling, then restore it after reinstalling.",
            "privacy.backup.choose": "Restore from backup",
            "privacy.backup.reading": "Checking\u2026",
            "privacy.backup.previewTitle": "Restore preview",
            "privacy.backup.legacy": "Legacy backup: only tasks, pages, and relationships will be replaced. New Tab sites and the Web Dock stay unchanged.",
            "privacy.backup.section.tasks": "task data",
            "privacy.backup.section.newtab": "New Tab sites",
            "privacy.backup.section.dock": "Web Dock",
            "privacy.backup.section.none": "nothing restorable",
            "privacy.backup.willOverwrite": "Will replace: {sections}",
            "privacy.backup.count.tasks": "Tasks",
            "privacy.backup.count.pages": "Pages",
            "privacy.backup.count.relations": "Relationships",
            "privacy.backup.count.categories": "New Tab categories",
            "privacy.backup.count.sites": "Saved sites",
            "privacy.backup.count.dockSites": "Web Dock sites",
            "privacy.backup.overwriteWarning": "Restore replaces the local data above. It never restores API keys, activation data, browser history, or current real tabs.",
            "privacy.backup.cancel": "Cancel",
            "privacy.backup.confirm": "Replace and restore",
            "privacy.backup.importing": "Restoring\u2026",
            "privacy.backup.importSuccess": "Restore complete. Tasks, New Tab sites, and the Web Dock are synchronized.",
            "privacy.backup.error.tooLarge": "The backup file must be 5 MiB or smaller.",
            "privacy.backup.error.invalidJson": "The selected file is not valid JSON.",
            "privacy.backup.error.invalidFormat": "The selected file is not a supported TabFlow backup.",
            "privacy.backup.error.unsupportedVersion": "This backup version is not supported yet. Update TabFlow and try again.",
            "privacy.backup.error.sensitiveData": "For privacy, this file contains sensitive data that cannot be restored.",
            "privacy.backup.error.unknownField": "The backup contains fields this version does not support.",
            "privacy.backup.error.invalidContent": "The backup is damaged or does not match the current format.",
            "privacy.backup.error.duplicate": "The backup contains duplicate records and cannot be restored safely.",
            "privacy.backup.error.brokenReference": "A task relationship points to missing data.",
            "privacy.backup.error.primaryConflict": "A page has more than one primary task in this backup.",
            "privacy.backup.error.sectionUnavailable": "This version cannot restore one of the included sections. No changes were made.",
            "privacy.backup.error.confirmationRequired": "Confirm that local data may be replaced first.",
            "privacy.backup.error.rolledBack": "Restore failed and the previous data was restored. Try again later.",
            "privacy.backup.error.partialRollback": "Restore failed and some previous data may not have been restored. Stop editing, keep the backup, reload, and verify the data.",
            "privacy.backup.error.inProgress": "Another restore is in progress. Wait for it to finish and try again.",
            "privacy.backup.error.previewRequired": "The restore preview expired. Select the backup file again and reconfirm.",
            "privacy.backup.error.reconcileFailed": "Data was written, but current-tab synchronization failed. Reload the extension and verify the data.",
            "privacy.backup.error.unavailable": "Backup and restore is temporarily unavailable. Try again later.",
            "privacy.backup.error.failed": "Could not restore the backup. Try again later.",
            "privacy.backup.error.readFailed": "Could not read the selected file. Choose it again.",
            "workbench.taskPages": "{count} related pages",
            "workbench.globalCount": "{tabs} tabs \xB7 {tasks} tasks",
            "workbench.localReady": "Local workspace ready",
            "workbench.footerHint": "Click a tab to switch \xB7 Search filters both panes",
            "workbench.unknownPage": "Unknown page",
            "workbench.sessionDwell": "Dwell {time}",
            "workbench.pageOpenNow": "Open in browser",
            "workbench.pageHistory": "History page; click to reopen",
            "workbench.previewImageAlt": "Page preview",
            "workbench.previewLoading": "Loading secure preview\u2026",
            "workbench.previewUnavailable": "Preview unavailable",
            "workbench.previewRestricted": "Browser-restricted pages cannot be previewed",
            "workbench.previewActivateRequired": "Switch to this tab and wait for it to load to generate a preview",
            "workbench.previewWaitingForLoad": "A preview will be generated after the page finishes loading",
            "workbench.openNow": "Open now",
            "workbench.previewSwitchHint": "Click card to switch",
            "workbench.columns": "Columns",
            "workbench.columnsAria": "Tabs per row",
            "workbench.columnsValue": "{count} columns",
            "workbench.noTabs": "No matching tabs",
            "workbench.noTabsHint": "Try another keyword or window",
            "workbench.noTasks": "No matching tasks",
            "workbench.noTasksHint": "Adjust search, domain, or status",
            "workbench.emptyTask": "No pages in this task yet",
            "workbench.emptyTaskHint": "You can add tabs in a later step",
            "workbench.recycleTitle": "Recycle bin",
            "workbench.resizePanes": "Resize panes",
            "workbench.collapseBrowserPane": "Collapse browser tabs pane",
            "workbench.collapseTaskPane": "Collapse task pane",
            "workbench.restoreSplitPane": "Restore split overview",
            "workbench.recycleHint": "Sorted by closing time; restore tabs, tab groups, or tasks",
            "recycle.restoreAll": "Restore all",
            "recycle.recently": "Just now",
            "recycle.minutesAgo": "{count} min ago",
            "recycle.today": "Today",
            "recycle.group": "Tab group",
            "recycle.task": "Task group",
            "recycle.closedTab": "Closed tab",
            "recycle.restore": "Restore",
            "recycle.delete": "Delete",
            "recycle.deletePermanently": "Delete permanently",
            "workbench.recycleRestoreAll": "Restore all",
            "workbench.recycleEmpty": "Recycle bin is empty",
            "workbench.recycleEmptyHint": "Closed tabs, tab groups, and dissolved tasks appear here",
            "workbench.recycleTask": "Task group",
            "workbench.recycleTab": "Tab",
            "workbench.recycleGroup": "Tab group",
            "workbench.recycleRestore": "Restore",
            "workbench.recycleDelete": "Delete permanently",
            "common.close": "Close",
            "common.theme": "Toggle theme",
            "common.settings": "Settings",
            "theme.title": "Theme",
            "theme.system": "Use system",
            "theme.light": "Light",
            "theme.dark": "Dark",
            "theme.changed": "Theme changed to {theme}",
            "overlay.sticky": "Pinned open",
            "overlay.title": "Tab workspace",
            "overlay.subtitle": "Search, organize, group, restore, and preview in one place",
            "overlay.membership": "Membership status",
            "overlay.activated": "Permanently activated",
            "overlay.close": "Close overlay (Esc)",
            "overlay.searchPlaceholder": "Search tab title, domain, or URL",
            "overlay.selectedNone": "0 selected",
            "overlay.action.newTitle": "Open a new tab from popular bookmarks",
            "overlay.action.new": "New",
            "overlay.action.groupDomainsTitle": "Group all tabs by primary domain",
            "overlay.action.groupDomains": "Group domains",
            "overlay.action.ungroupAllTitle": "Remove every tab group",
            "overlay.action.ungroupAll": "Ungroup",
            "overlay.action.recycleTitle": "View recycle bin",
            "overlay.action.recycle": "Recycle bin",
            "overlay.action.undoTitle": "Undo close and restore the latest item",
            "overlay.action.undo": "Undo",
            "overlay.footer.escapeClose": "Close",
            "overlay.footer.holdOpen": "Hold the shortcut to open",
            "overlay.footer.stickyBefore": "Hold longer than",
            "overlay.footer.stickyAfter": "seconds and release to keep open",
            "overlay.footer.holdDuration": "Hold duration",
            "overlay.footer.hints": "Click to switch \xB7 Hover to preview \xB7 Drag to reorder \xB7 Right-click to select",
            "overlay.layout.aria": "Tab layout settings",
            "overlay.layout.perRow": "Per row",
            "overlay.layout.perRowAria": "Tabs per row",
            "overlay.layout.columns": "{count} columns",
            "overlay.layout.viewAria": "Tab view",
            "overlay.layout.hover": "Compact",
            "overlay.layout.card": "Cards",
            "overlay.layout.hoverPreview": "Hover preview",
            "overlay.selection.groupTitle": "Put selected tabs in a new group",
            "overlay.selection.group": "Group selected",
            "overlay.selection.closeTitle": "Close selected tabs",
            "overlay.selection.close": "Close selected",
            "overlay.selection.clearTitle": "Clear selection",
            "overlay.selection.clear": "Cancel",
            "overlay.loading": "Loading\u2026",
            "overlay.count.recycle": "Recycle bin \xB7 {count} items",
            "overlay.count.tabs": "{count} tabs",
            "overlay.count.windows": "{count} windows",
            "overlay.count.selected": "{selected} selected / {total}",
            "dock.label": "Web Dock",
            "dock.expand": "Expand Web Dock",
            "dock.addSite": "Add site",
            "dock.search": "Web search",
            "dock.settings": "Web Dock settings",
            "dock.settingsShort": "Dock settings",
            "dock.settings.empty": "No pinned sites yet",
            "dock.settings.fixedSites": "Pinned sites",
            "dock.settings.iconSize": "Icon size",
            "dock.settings.magnification": "Hover magnification",
            "dock.settings.influenceRadius": "Influence radius",
            "dock.settings.response": "Response",
            "dock.settings.damping": "Damping",
            "dock.settings.hideDelay": "Hide delay",
            "dock.settings.tileSize": "Icon tile size",
            "dock.settings.trayPadding": "Tray padding",
            "dock.settings.tileOpacity": "Tile opacity",
            "dock.settings.tileColor": "Icon tile color",
            "dock.settings.enabled": "Enable Web Dock",
            "dock.settings.enabledHint": "Show favorite sites at the bottom of every webpage",
            "dock.settings.autoHide": "Auto-hide",
            "dock.settings.autoHideHint": "Collapse the Dock after the pointer leaves",
            "dock.settings.appearance": "Appearance",
            "dock.settings.motion": "Motion",
            "dock.settings.preview": "Preview",
            "dock.settings.previewAria": "Web Dock preview",
            "dock.settings.reset": "Reset defaults",
            "dock.settings.siteName": "Site name",
            "dock.settings.siteUrl": "Site address",
            "dock.settings.addSite": "Add site",
            "dock.settings.drag": "Drag to reorder",
            "dock.settings.save": "Save",
            "dock.settings.delete": "Delete",
            "dock.settings.platformNote": "Note: Some sites restrict icon access. TabFlow automatically uses a safe fallback.",
            "dock.settings.saveHint": "Changes preview live. Select Done to close settings.",
            "dock.settings.done": "Done",
            "dock.settings.updated": "Site updated",
            "dock.settings.added": "Site added",
            "dock.settings.saveFailed": "Could not save. Try again.",
            "dock.settings.addFailed": "Could not add the site. Try again.",
            "dock.settings.saveDirtyFirst": "Save the current edit first",
            "dock.recent": "Recently closed",
            "dock.recentSites": "Recently closed sites",
            "dock.recentLoading": "Loading recently closed sites\u2026",
            "dock.recentEmpty": "No sites available to restore",
            "dock.recentUnavailable": "Recently closed sites are temporarily unavailable",
            "dock.recentRestoreAria": "Restore {title}",
            "dock.recentRestoreFailed": "Could not restore the site. Try again.",
            "dock.recentSyncFailed": "The site opened, but the recently closed list could not sync.",
            "dock.recentRestored": "Site restored",
            "dock.menu.background": "Open in new background tab",
            "dock.menu.edit": "Edit",
            "dock.menu.delete": "Delete",
            "dock.openBackgroundFailed": "Could not open the site in the background. Try again.",
            "dock.openFailed": "Could not open the site. Try again.",
            "dock.deleteFailed": "Could not delete the site. Try again.",
            "dock.deleted": "Site deleted",
            "dock.orderSaved": "Site order saved",
            "dock.orderSaveFailed": "Could not save the order. Try again.",
            "dock.pinFailed": "Could not update the pinned state. Try again.",
            "dock.pinned": "Pinned to Web Dock",
            "dock.unpinned": "Unpinned",
            "dock.extensionUpdated": "The extension was updated. Refresh this page and try again.",
            "dock.duplicate": "This site is already in Web Dock",
            "activation.locale": "en-US",
            "activation.localRecord": "Local record",
            "activation.loadFailed": "Could not load activation status. Try again.",
            "activation.purchaseGuide": "Xianyu purchase guide",
            "activation.purchaseActiveHint": "Collapsed after activation; you can expand it anytime",
            "activation.purchaseHint": "Scan to purchase a permanent activation code",
            "activation.expandImage": "Show image",
            "activation.purchaseAria": "Purchase an activation code",
            "activation.zoomAria": "Enlarge the Xianyu purchase guide",
            "activation.imageAlt": "Xianyu shop purchase guide and QR code",
            "activation.purchaseEyebrow": "Purchase guide",
            "activation.noCode": "Need an activation code?",
            "activation.purchaseCopy": "Save or enlarge the image, then scan it with Xianyu to open the shop. You will receive a permanent activation code after purchase.",
            "activation.zoom": "Enlarge",
            "activation.collapseImage": "Hide image",
            "activation.memberStatus": "Membership status",
            "activation.active": "Permanently activated",
            "activation.unlocked": "Premium features are unlocked",
            "activation.used": "This activation code has been used and retired",
            "activation.activatedAt": "Activated: {time}",
            "activation.deactivate": "Deactivate",
            "activation.invalid": "The activation code is invalid. Check it and try again.",
            "activation.eyebrow": "Permanent activation",
            "activation.inputTitle": "Enter the activation code received after purchase",
            "activation.copy": "No account is required. Verification runs online and activation is saved permanently on this device. Re-enter the same code after reinstalling to restore access.",
            "activation.code": "Activation code",
            "activation.verifying": "Verifying\u2026",
            "activation.submit": "Verify and activate",
            "activation.progress": "Connecting and verifying your activation code",
            "activation.offline": "You are offline. Connect to the internet and try again.",
            "activation.failed": "Activation failed. Try again later.",
            "activation.previewAria": "Purchase guide image preview",
            "activation.previewClose": "Close purchase guide image",
            "activation.deactivateClose": "Close deactivation confirmation",
            "activation.deactivateTitle": "Deactivate this device?",
            "activation.deactivateCopy": "Your membership badge and premium benefits will stop immediately. You can reactivate later with the same code.",
            "activation.cancel": "Cancel",
            "activation.confirmDeactivate": "Deactivate",
            "activation.deactivating": "Deactivating\u2026",
            "activation.deactivated": "Activation removed",
            "activation.deactivateFailed": "Could not deactivate. Try again later.",
            "dock.add.catalogQuick": "Choose a common site",
            "dock.add.siteCount": "{count} sites",
            "dock.add.catalogSearchPlaceholder": "Search sites or categories",
            "dock.add.catalogCategories": "Common site categories",
            "dock.add.editTitle": "Edit site",
            "dock.add.addTitle": "Add site",
            "dock.add.editDescription": "Update the site title, address, and icon",
            "dock.add.addDescription": "Save this page or add any commonly used site",
            "dock.add.close": "Close add-site dialog",
            "dock.add.modeLabel": "Add method",
            "dock.add.mode.current": "Save current page",
            "dock.add.mode.custom": "Add manually",
            "dock.add.mode.catalog": "Choose common site",
            "dock.add.mode.bookmarks": "Import bookmarks",
            "dock.add.bookmarksHeading": "Choose Chrome bookmarks",
            "dock.add.bookmarkCount": "{count} bookmarks",
            "dock.add.bookmarkSelected": "{count} selected",
            "dock.add.bookmarkSearchPlaceholder": "Search title, address, or folder",
            "dock.add.bookmarksLoading": "Loading Chrome bookmarks\u2026",
            "dock.add.bookmarksList": "Chrome bookmark list",
            "dock.add.bookmarksNoMatch": "No matching bookmarks",
            "dock.add.bookmarksEmpty": "No web bookmarks available to import",
            "dock.add.bookmarksLoadFailed": "Could not load bookmarks. Try again.",
            "dock.add.previewAria": "Live icon preview",
            "dock.add.previewCaption": "Live preview",
            "dock.add.previewTitle": "Site title",
            "dock.add.previewHint": "If the best automatic icon is unavailable, TabFlow falls back to another icon and then text.",
            "dock.add.cancel": "Cancel",
            "dock.add.save": "Save to Dock",
            "dock.add.import": "Import to Dock",
            "dock.add.saving": "Saving\u2026",
            "dock.add.currentPage": "Current page",
            "dock.add.untitledBookmark": "Untitled bookmark",
            "dock.add.otherBookmarks": "Other bookmarks",
            "dock.add.selectBookmark": "Select at least one bookmark",
            "dock.add.allDuplicates": "All {count} selected sites are already in Web Dock",
            "dock.add.importFailed": "Import failed. Try again.",
            "dock.add.importPartial": "Imported {count}, then failed: {reason}",
            "dock.add.imported": "Imported {count} bookmarks{suffix}",
            "dock.add.skipped": "; skipped {count} already-pinned sites",
            "dock.add.titleRequired": "Enter a site title",
            "dock.add.urlInvalid": "Enter a valid web address",
            "dock.add.textIconInvalid": "A text icon must contain 1\u201310 letters",
            "dock.add.saveFailed": "Could not save the site. Try again.",
            "dock.add.saveFailedWithReason": "Could not save: {reason}",
            "dock.add.imageRequired": "Choose a local image first",
            "dock.add.updated": "Site updated",
            "dock.add.added": "Site added",
            "dock.add.imageReadingFailed": "Could not read the image. Try again.",
            "dock.add.imageUnrecognized": "This image could not be recognized",
            "dock.add.imageTypeInvalid": "Use a PNG, JPEG, WebP, or SVG image",
            "dock.add.imageTooLarge": "The image must be no larger than 512 KB",
            "dock.add.imageSizeInvalid": "The image dimensions are invalid",
            "dock.add.imageUnavailable": "This page cannot process the image",
            "dock.add.imageCompressedTooLarge": "The compressed image is still too large. Choose a simpler image.",
            "dock.add.imageProcessing": "Processing image\u2026",
            "dock.add.imageUploadHint": "PNG / JPEG / WebP / SVG, up to 512 KB",
            "dock.add.imageProcessFailed": "Could not process the image",
            "dock.search.engineAria": "Search with {engine}",
            "dock.search.source.common": "Favorite",
            "dock.search.source.fixed": "Pinned",
            "dock.search.source.tab": "Tab",
            "dock.search.source.bookmark": "Bookmark",
            "dock.search.source.history": "History",
            "dock.search.source.web": "Web",
            "dock.search.source.unknown": "Other",
            "dock.search.recents": "Recent searches",
            "dock.search.resultCount": "{count} items",
            "dock.search.noRecents": "No recent searches. Type a query to begin.",
            "dock.search.results": "Related results",
            "dock.search.sources": "Tabs \xB7 Bookmarks \xB7 History",
            "dock.search.emptyPermission": "No available matches. Allow access to include more results.",
            "dock.search.empty": "No matches. Press Enter to search with the selected engine.",
            "dock.search.openPageFailed": "Could not open the search page. Try again.",
            "dock.search.openResultFailed": "Could not open the search result. Try again.",
            "dock.search.title": "Web search",
            "dock.search.description": "Quickly search current tabs, bookmarks, and browsing history",
            "dock.search.close": "Close web search",
            "dock.search.placeholder": "Search web pages, tabs, or bookmarks",
            "dock.search.clear": "Clear search",
            "dock.search.chooseEngine": "Choose search engine",
            "siteEditor.category": "Category",
            "siteEditor.title": "Site title",
            "siteEditor.url": "Web address",
            "siteEditor.icon": "Site icon",
            "siteEditor.iconSource": "Icon source",
            "siteEditor.iconAuto": "Fetch automatically",
            "siteEditor.iconText": "Text icon",
            "siteEditor.iconUpload": "Local upload",
            "siteEditor.iconTextLabel": "Icon text",
            "siteEditor.iconTextPlaceholder": "Up to 10 letters",
            "siteEditor.iconColor": "Text icon color",
            "siteEditor.uploadChoose": "Choose a local image",
            "siteEditor.uploadHint": "PNG / JPEG / WebP / SVG, up to 512 KB",
            "tabList.untitled": "(Untitled)",
            "tabList.unnamedGroup": "Untitled group",
            "tabList.dockUnpin": "Unpin from Web Dock",
            "tabList.dockPin": "Pin to Web Dock",
            "tabList.previewAlt": "{title} preview",
            "tabList.previewLoading": "Loading preview\u2026",
            "tabList.pinned": "Pinned",
            "tabList.close": "Close tab",
            "tabList.switch": "Switch",
            "tabList.duplicate": "Duplicate",
            "tabList.select": "Select",
            "tabList.unselect": "Unselect",
            "tabList.unpin": "Unpin",
            "tabList.pin": "Pin",
            "tabList.removeGroup": "Remove from group",
            "tabList.addGroup": "Add to group",
            "tabList.currentWindow": "Current window",
            "tabList.active": "Current tab",
            "tabList.window": "Window {number}{state}",
            "tabList.window.minimized": " [minimized]",
            "tabList.window.maximized": " [maximized]",
            "tabList.window.fullscreen": " [full screen]",
            "tabList.editGroup": "Edit group",
            "tabList.edit": "Edit",
            "tabList.dissolveGroup": "Dissolve group",
            "tabList.group": "Group",
            "tabList.tab": "Tab",
            "tabList.tabCount": "{count} tabs",
            "tabList.restore": "Restore",
            "tabList.delete": "Delete",
            "tabList.groupNamePlaceholder": "Group name (optional)",
            "tabList.cancel": "Cancel",
            "tabList.searchEmpty": "No tabs match \u201C{query}\u201D",
            "tabList.searchHint": "Try a domain, page title, or group name",
            "tabList.empty": "No manageable tabs yet",
            "tabList.emptyHint": "Open more pages or try again later",
            "tabList.pinnedSection": "Pinned ({count})",
            "tabList.ungroupedSection": "Ungrouped ({count})",
            "tabList.expandMore": "Show {count} more tabs",
            "tabList.collapse": "Collapse",
            "tabList.recycle": "Recycle bin ({count})",
            "tabList.clear": "Clear",
            "tabList.back": "Back to tabs",
            "tabList.recycleEmpty": "Recycle bin is empty",
            "tabList.recycleHint": "Closed tabs and groups stay here until restored or deleted",
            "tabList.selected": "{count} selected",
            "permission.bookmarks.required": "Bookmark access is required to import bookmarks. You can try again later.",
            "permission.bookmarks.allow": "Allow bookmarks",
            "permission.bookmarks.denied": "Bookmark access was not granted. Nothing was imported, and you can try again later.",
            "permission.bookmarks.unsupported": "This browser does not support requesting bookmark access on demand.",
            "permission.history.required": "History access is required to show history results. Current-tab search still works.",
            "permission.history.allow": "Allow browser history",
            "permission.history.denied": "History access was not granted. Current-tab search still works, and you can try again later.",
            "permission.history.unsupported": "This browser does not support requesting history access on demand.",
            "ai.settings.nav": "AI organization",
            "ai.adjustment.summary": "AI adjusted {count} items",
            "ai.adjustment.view": "View",
            "ai.adjustment.hide": "Hide",
            "ai.adjustment.undo": "Undo",
            "ai.adjustment.undoing": "Undoing",
            "ai.adjustment.undone": "Undone",
            "ai.adjustment.close": "Close details",
            "ai.adjustment.panelTitle": "AI adjustment details",
            "ai.adjustment.before": "Before",
            "ai.adjustment.after": "After",
            "ai.adjustment.reason": "Why",
            "ai.adjustment.create_ai_task": "Created task",
            "ai.adjustment.rename_ai_task": "Renamed task",
            "ai.adjustment.assign_ai_relation": "Assigned page",
            "ai.adjustment.reorder_ai_tasks": "Reordered tasks",
            "ai.adjustment.conflict": "Cannot undo because you changed related items after the AI adjustment.",
            "ai.adjustment.failed": "Undo failed. Please try again.",
            "ai.adjustment.empty": "No AI adjustments to show.",
            "ai.settings.title": "AI organization",
            "ai.settings.description": "Configure virtual task organization, model access, and your local key",
            "ai.settings.scopeTitle": "AI only organizes virtual tasks",
            "ai.settings.scope": "AI may update virtual task relationships and AI-managed tasks, but it never opens, closes, moves, groups, or reorders real tabs.",
            "ai.settings.enabled": "Enable AI organization",
            "ai.settings.enabledHint": "When off, no cloud requests are made and local suggestions still work",
            "ai.settings.autoOrganize": "Auto-apply high-confidence changes",
            "ai.settings.autoOrganizeHint": "Only virtual task changes that pass local safety checks are applied",
            "ai.settings.channelTitle": "Model provider",
            "ai.settings.channelHint": "OpenAI-compatible API; the address and model are editable",
            "ai.settings.provider": "OpenAI compatible",
            "ai.settings.baseUrl": "API address",
            "ai.settings.model": "Model",
            "ai.settings.apiKey": "API Key",
            "ai.settings.keyPreserved": "A key is saved; leave blank to keep using it",
            "ai.settings.keyPlaceholder": "Enter API Key",
            "ai.settings.showKey": "Show key",
            "ai.settings.hideKey": "Hide key",
            "ai.settings.clearKey": "Clear key",
            "ai.settings.localKey": "The key stays on this device. It is not synced, exported, logged, or shown in notifications.",
            "ai.settings.previewTitle": "Preview fields sent to AI",
            "ai.settings.previewHint": "Only a locally filtered, minimal candidate set is sent when AI is enabled.",
            "ai.settings.previewPage": "Current page",
            "ai.settings.previewPageFields": "Redacted title, registered domain, and path type",
            "ai.settings.previewTasks": "Candidate tasks",
            "ai.settings.previewTaskFields": "Task name, domain, and a few redacted core-page titles",
            "ai.settings.previewSignals": "Local signals",
            "ai.settings.previewSignalFields": "Aggregated evidence codes, risk codes, and counts",
            "ai.settings.previewNever": "Full URLs, query parameters, page content, typed input, cookies, screenshots, raw behavior events, and the API Key are never sent.",
            "ai.settings.actionHint": "Test the connection before saving changes",
            "ai.settings.testConnection": "Test connection",
            "ai.settings.save": "Save AI settings",
            "ai.settings.loading": "Loading local settings\u2026",
            "ai.settings.saving": "Saving local settings\u2026",
            "ai.settings.testing": "Testing connection\u2026",
            "ai.settings.saved": "AI settings saved on this device.",
            "ai.settings.savedPermissionPending": "Settings saved; allow access to this address before testing the connection.",
            "ai.settings.testSuccess": "Connection successful. This provider is ready.",
            "ai.settings.keyWillClear": "The local key will be cleared when you save.",
            "ai.settings.error.permission": "Access to this address was not granted. You can try again later.",
            "ai.settings.error.unauthorized": "The provider rejected this key. Check or replace the key.",
            "ai.settings.error.paymentRequired": "The provider account has insufficient credit or quota.",
            "ai.settings.error.forbidden": "The provider denied access to this endpoint or model. Check account and model access.",
            "ai.settings.error.quotaOrAccess": "The provider account needs credit or access to this model.",
            "ai.settings.error.upstreamReason": "Provider reason: ",
            "ai.settings.error.badRequest": "The provider rejected the model or request parameters. Check the model name.",
            "ai.settings.error.notFound": "The API address or model endpoint is unavailable. Check the address and model name.",
            "ai.settings.error.rateLimit": "Too many requests. Try again later.",
            "ai.settings.error.server": "The provider is temporarily unavailable. Try again later.",
            "ai.settings.error.timeout": "Connection timed out. Check the address or network.",
            "ai.settings.error.network": "Could not connect. Check the address and network.",
            "ai.settings.error.baseUrl": "The API address must be a valid HTTPS address.",
            "ai.settings.error.invalidSettings": "The AI provider settings are invalid. Check the address, model, and key.",
            "ai.settings.error.invalidResponse": "The provider returned an unreadable response or one that does not match the expected API format.",
            "ai.settings.error.provider": "The provider rejected the request without a recognizable error type.",
            "ai.settings.error.keyRequired": "Enter an API Key or keep the saved key.",
            "ai.settings.error.model": "Enter a model name.",
            "ai.settings.error.load": "Could not load AI settings. Try again.",
            "ai.settings.error.save": "Could not save AI settings. Try again.",
            "ai.settings.error.test": "Connection test failed. Check the settings and try again.",
            "task.newPrompt": "Enter a task name",
            "task.renameHint": "Click to edit title",
            "task.renameAria": "Edit task title: {title}",
            "task.expandPages": "Show all tabs",
            "task.collapsePages": "Collapse to one row",
            "task.undoLastChange": "Undo last task change",
            "task.dragToReorder": "Drag to reorder tasks",
            "task.dragDisabledByFilter": "Use manual order and clear filters to drag",
            "task.moveUp": "Move task up",
            "task.moveDown": "Move task down",
            "task.openAll": "Open all",
            "task.closeAll": "Close all",
            "task.more": "More",
            "task.dissolve": "Dissolve task",
            "task.removePage": "Remove from task",
            "task.source.manual": "Manual classification",
            "task.source.smart": "Smart classification",
            "suggestion.source.local": "Local suggestion",
            "suggestion.source.ai": "AI suggestion",
            "suggestion.confidence.high": "High confidence",
            "suggestion.confidence.medium": "Medium confidence",
            "suggestion.reason.opener_link": "Opened from a page in this task",
            "suggestion.reason.stable_object": "Uses the same work item as this task",
            "suggestion.reason.qualified_return": "Frequently switched between this page and the task",
            "suggestion.reason.task_snapshot_restore": "Restored with this task",
            "suggestion.reason.native_group_restore": "Restored with task pages from the same tab group",
            "suggestion.reason.title_overlap": "Title matches this task",
            "suggestion.reason.same_native_group": "In the same tab group as task pages",
            "suggestion.reason.same_window": "In the same window as task pages",
            "suggestion.reason.same_domain": "Uses the same site as task pages",
            "suggestion.reason.risk_suppressed": "Confidence lowered due to uncertainty",
            "suggestion.reason.ambiguous": "Other close matches are available",
            "suggestion.action.addToTask": "Add to task",
            "suggestion.action.alsoLink": "Also link",
            "suggestion.action.reject": "Reject",
            "suggestion.action.undo": "Undo",
            "suggestion.batch.enter": "Batch process",
            "suggestion.batch.select": "Select this suggestion",
            "suggestion.batch.selectAll": "Select current suggestions",
            "suggestion.batch.count": "{count} selected",
            "suggestion.batch.accept": "Add selected to tasks",
            "suggestion.batch.reject": "Reject selected",
            "suggestion.batch.cancel": "Cancel batch processing",
            "suggestion.batch.running": "Processing {count} suggestions",
            "suggestion.batch.limit": "Select up to {count} suggestions at once",
            "suggestion.label.addToTask": "Add to \u201C{task}\u201D",
            "suggestion.label.chooseTask": "Choose the task that best matches your intent",
            "suggestion.label.choosePrimary": "Choose the primary task for this page",
            "suggestion.label.multiAssociate": "This page may also belong to these tasks",
            "suggestion.label.relatedPage": "\u201C{page}\u201D has additional task candidates",
            "suggestion.notice.rejected": "Suggestion rejected",
            "suggestion.notice.accepted": "Added to task",
            "suggestion.state.loading": "Analyzing",
            "suggestion.aria.chooseTask": "Choose task: {task}",
            "suggestion.aria.undoAccepted": "Undo smart classification: {task}",
            "suggestion.error.expired": "Suggestion expired",
            "suggestion.error.conflict": "Something changed. Try again.",
            "suggestion.error.failed": "Suggestion action failed. Try again.",
            "suggestion.why": "Why this suggestion",
            "toast.suggestionAccepted": "Added to task",
            "toast.suggestionRejected": "Suggestion rejected",
            "toast.suggestionUndone": "Suggestion action undone",
            "toast.suggestionBatchSuccess": "Processed {count} suggestions",
            "toast.suggestionBatchPartial": "{succeeded} succeeded, {failed} failed",
            "task.pageMoveUp": "Move page up",
            "task.pageMoveDown": "Move page down",
            "task.chooseNewPrimary": "Choose a new primary task",
            "task.relation.primary": "Primary task",
            "task.relation.primaryHint": "The primary task for this page",
            "task.relation.assign": "Add to task\u2026",
            "task.relation.move": "Move to task\u2026",
            "task.relation.share": "Also link to task\u2026",
            "task.relation.makePrimary": "Make primary task",
            "task.relation.assignHint": "Choose the primary task for this page.",
            "task.relation.moveHint": "The target becomes primary and the current task link is removed. The real tab stays where it is.",
            "task.relation.shareHint": "Keep the current primary task and add another task link.",
            "task.relation.dropChoice": "Choose how to add to \u201C{task}\u201D",
            "task.relation.moveHere": "Move to this task",
            "task.relation.shareHere": "Also link to this task",
            "common.back": "Back",
            "task.confirmDissolve": "Dissolve this task? Real tabs will stay open, and the task group can be restored from the recycle bin.",
            "task.confirmDeleteTrash": "Delete this recycled item permanently? This cannot be undone.",
            "toast.taskAssigned": "Added to task manually",
            "toast.taskRelationMoved": "Task link moved; the real tab stayed in place",
            "toast.taskRelationShared": "Page linked to another task",
            "toast.taskPrimaryChanged": "Primary task updated",
            "toast.taskRemoved": "Removed from task without closing the real tab",
            "toast.taskStatusChanged": "Task status updated",
            "toast.taskDomainChanged": "Task domain updated",
            "toast.taskReordered": "Task order updated",
            "toast.taskPagesReordered": "Task page order updated",
            "toast.taskCreated": "Task created",
            "toast.taskRenamed": "Task updated",
            "toast.taskMissing": "This task no longer exists. Close the dialog and try again.",
            "toast.taskDissolved": "Task dissolved and recoverable; real tabs remain open",
            "toast.taskRestored": "Task group restored",
            "toast.trashRestored": "Recycled item restored",
            "toast.trashRestoreAll": "Recycle bin restored",
            "toast.trashDeleted": "Recycled item permanently deleted",
            "toast.trashLoadFailed": "Could not load the recycle bin. Please try again.",
            "toast.trashActionFailed": "Recycle bin action failed. Please try again.",
            "toast.taskOpened": "Task pages opened",
            "toast.taskAlreadyOpen": "All task pages are already open",
            "toast.taskNoOpenTabs": "No open task tabs to close",
            "toast.taskTabsClosed": "Open task tabs closed",
            "toast.taskChangeFailed": "Task update failed",
            "toast.taskChangeUndone": "Last task change undone",
            "toast.taskOperationPending": "The operation is still syncing. Reopen the panel shortly to verify.",
            "toast.taskTrashCleanupPending": "The task was restored, but its trash record is still being cleaned up.",
            "toast.tabPinned": "Tab pinned",
            "toast.tabUnpinned": "Tab unpinned",
            "toast.tabCommandFailed": "Tab action failed. Please try again.",
            "toast.tabNotOpen": "This task page is not currently open",
            "toast.taskOpenFailed": "Could not open task pages. Please try again.",
            "toast.taskCloseFailed": "Could not close task tabs. Please try again.",
            "toast.layoutSyncFailed": "Layout settings could not be synchronized",
            "tab.activate": "Switch to {title}",
            "tab.close": "Close tab",
            "tab.pinned": "Pinned",
            "tab.dockFixed": "Pinned to Web Dock",
            "tab.more": "More actions",
            "tab.removeFromDock": "Unpin from Web Dock",
            "tab.addToDock": "Pin to Web Dock",
            "task.untitled": "Untitled task",
            "task.status.active": "Active",
            "task.status.paused": "Paused",
            "task.status.done": "Done",
            "task.status.pending": "Inbox",
            "domain.work": "Work",
            "domain.study": "Study",
            "domain.life": "Life",
            "domain.interest": "Interests",
            "domain.admin": "Admin",
            "domain.other": "Other",
            "guidance.eyebrow": "Proactive reminder",
            "guidance.close": "Dismiss reminder",
            "guidance.muteToday": "Not again today",
            "guidance.openWorkbench": "Hold Alt or click to open",
            "guidance.frequent.title": "Switching fast? Find the right tab at a glance.",
            "guidance.frequent.message": "Find the pages you are switching between at a glance.",
            "guidance.frequent.action": "Hold Alt or click to open",
            "guidance.startup.title": "Pick up where you left off. TabFlow is ready.",
            "guidance.startup.message": "Pick up where you left off. TabFlow is ready.",
            "guidance.startup.action": "Hold Alt or click to open",
            "guidance.20.title": "More tabs open? A task view keeps them clear.",
            "guidance.20.message": "More tabs open? A task view keeps them clear.",
            "guidance.20.action": "Hold Alt or click to open",
            "guidance.30.title": "Tab bar getting crowded? TabFlow finds pages fast.",
            "guidance.30.message": "Tab bar getting crowded? TabFlow finds pages fast.",
            "guidance.30.action": "Hold Alt or click to open",
            taskDialogCreateTitle: "New task",
            taskDialogRenameTitle: "Edit task",
            taskDialogTitleLabel: "Task name",
            taskDialogTitlePlaceholder: "Enter a task name",
            taskDialogDomainLabel: "Choose a domain",
            taskDialogCreate: "Create task",
            taskDialogSave: "Save changes",
            taskDialogSaving: "Saving\u2026",
            taskDialogRequired: "Enter a task name",
            taskDialogSubmitFailed: "Could not save. Try again.",
            taskDomain_work: "Work",
            taskDomain_study: "Study",
            taskDomain_life: "Life",
            taskDomain_interest: "Interests",
            taskDomain_admin: "Admin",
            taskDomain_other: "Other",
            commonCancel: "Cancel"
          })
        });
        function normalizeLanguage(value) {
          const language = String(value || "").trim().toLowerCase();
          return language.startsWith("zh") ? "zh" : "en";
        }
        function resolveLanguage({ manual, browserLanguage, navigatorLanguage } = {}) {
          if (manual === "zh" || manual === "zh-CN") return "zh";
          if (manual === "en") return "en";
          return normalizeLanguage(browserLanguage || navigatorLanguage || "en");
        }
        function createTranslator(language, messages = MESSAGES) {
          const locale = normalizeLanguage(language);
          return function translate(key, fallback = key) {
            const safeFallback = typeof fallback === "string" ? fallback : key;
            return messages[locale]?.[key] ?? messages.en?.[key] ?? safeFallback;
          };
        }
        return Object.freeze({ LANGUAGE_STORAGE_KEY, MESSAGES, normalizeLanguage, resolveLanguage, createTranslator });
      });
    }
  });

  // src/content/content-ui-logic.js
  var require_content_ui_logic = __commonJS({
    "src/content/content-ui-logic.js"(exports, module) {
      (function attachContentUiLogic(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaContentUiLogic = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createContentUiLogic() {
        "use strict";
        function hostOf(url) {
          try {
            return new URL(url).hostname.replace(/^www\./, "");
          } catch (_error) {
            return "";
          }
        }
        function letterOf(url) {
          const host = hostOf(url);
          return host ? host[0].toUpperCase() : "?";
        }
        function domainColor(url) {
          const host = hostOf(url) || url || "x";
          let hash = 0;
          for (let index = 0; index < host.length; index += 1) {
            hash = (hash << 5) - hash + host.charCodeAt(index) | 0;
          }
          return `hsl(${Math.abs(hash) % 360}, 55%, 45%)`;
        }
        function escapeHtml(value) {
          return (value || "").replace(/[&<>"']/g, (character) => ({
            "&": "&amp;",
            "<": "&lt;",
            ">": "&gt;",
            '"': "&quot;",
            "'": "&#39;"
          })[character]);
        }
        function maskCodeForDisplay(code) {
          const compact = String(code || "").toUpperCase().replace(/[^A-Z0-9]/g, "");
          const prefix = compact.slice(0, 2) || "AK";
          const suffix = compact.slice(-4) || "\u2022\u2022\u2022\u2022";
          return `${prefix}-\u2022\u2022\u2022\u2022-\u2022\u2022\u2022\u2022-\u2022\u2022\u2022\u2022-\u2022\u2022\u2022\u2022-${suffix}`;
        }
        function serializedByteLength(value) {
          const serialized = JSON.stringify(value);
          if (typeof TextEncoder === "function") return new TextEncoder().encode(serialized).byteLength;
          return unescape(encodeURIComponent(serialized)).length;
        }
        return Object.freeze({
          hostOf,
          letterOf,
          domainColor,
          escapeHtml,
          maskCodeForDisplay,
          serializedByteLength
        });
      });
    }
  });

  // src/content/app-context.js
  var require_app_context = __commonJS({
    "src/content/app-context.js"(exports, module) {
      (function attachAppContext(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaAppContext = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createAppContextModule() {
        "use strict";
        function createAppContext() {
          return {
            selectedTabIds: /* @__PURE__ */ new Set(),
            collapsedWindows: /* @__PURE__ */ new Set(),
            limitedWindows: /* @__PURE__ */ new Set(),
            previewAttemptedTabIds: /* @__PURE__ */ new Set(),
            tabScreenshots: /* @__PURE__ */ new Map(),
            tabScreenshotKinds: /* @__PURE__ */ new Map()
          };
        }
        return Object.freeze({ createAppContext });
      });
    }
  });

  // src/content/lifecycle-runtime.js
  var require_lifecycle_runtime = __commonJS({
    "src/content/lifecycle-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaLifecycleRuntime = api;
        else if (!commonJs) root.TfaLifecycleRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createLifecycleRuntime() {
          const disposers = [];
          let destroyed = false;
          function register(dispose) {
            if (typeof dispose !== "function") throw new TypeError("lifecycle disposer must be a function");
            if (destroyed) {
              dispose();
              return false;
            }
            disposers.push(dispose);
            return true;
          }
          function listen(target, type, handler, options) {
            if (destroyed) return false;
            if (!target?.addEventListener || !target?.removeEventListener) {
              throw new TypeError("lifecycle listener target is invalid");
            }
            target.addEventListener(type, handler, options);
            return register(() => target.removeEventListener(type, handler, options));
          }
          function subscribe(event, handler) {
            if (destroyed) return false;
            if (!event?.addListener || !event?.removeListener) {
              throw new TypeError("lifecycle subscription target is invalid");
            }
            event.addListener(handler);
            return register(() => event.removeListener(handler));
          }
          function patchOwned(target, key, wrapperFactory) {
            if (destroyed) return null;
            if (!target || typeof wrapperFactory !== "function") throw new TypeError("lifecycle patch is invalid");
            const hadOwn = Object.prototype.hasOwnProperty.call(target, key);
            const descriptor = hadOwn ? Object.getOwnPropertyDescriptor(target, key) : null;
            const original = target[key];
            const wrapper = wrapperFactory(original);
            if (typeof wrapper !== "function") throw new TypeError("lifecycle patch wrapper must be a function");
            if (descriptor && descriptor.configurable === false && (!Object.prototype.hasOwnProperty.call(descriptor, "value") || descriptor.writable === false)) {
              throw new TypeError(`lifecycle patch ${String(key)} is not configurable or writable`);
            }
            const enumerable = descriptor?.enumerable ?? false;
            if (descriptor && descriptor.configurable === false) {
              Object.defineProperty(target, key, { ...descriptor, value: wrapper });
            } else {
              Object.defineProperty(target, key, {
                configurable: true,
                enumerable,
                writable: true,
                value: wrapper
              });
            }
            let restored = false;
            const restore = () => {
              if (restored) return false;
              restored = true;
              if (target[key] !== wrapper) return false;
              if (hadOwn) Object.defineProperty(target, key, descriptor);
              else delete target[key];
              return true;
            };
            register(restore);
            return Object.freeze({ value: wrapper, restore });
          }
          function patch(target, key, wrapperFactory) {
            return patchOwned(target, key, wrapperFactory)?.value || null;
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            while (disposers.length) {
              try {
                disposers.pop()();
              } catch (error) {
              }
            }
            return true;
          }
          function inspect() {
            return { destroyed, disposerCount: disposers.length };
          }
          return Object.freeze({ register, listen, subscribe, patch, patchOwned, destroy, inspect });
        }
        return { createLifecycleRuntime };
      });
    }
  });

  // src/content/overlay-template.js
  var require_overlay_template = __commonJS({
    "src/content/overlay-template.js"(exports, module) {
      (function attachOverlayTemplate(rootScope, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        rootScope.TfaOverlayTemplate = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createOverlayTemplate() {
        "use strict";
        const SEARCH_SVG = '<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>';
        const SETTINGS_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M12 3.5a2 2 0 0 1 2 2v.3a7 7 0 0 1 1.7 1l.3-.2a2 2 0 1 1 2 3.4l-.3.2c.1.6.1 1.2 0 1.8l.3.2a2 2 0 1 1-2 3.4l-.3-.2a7 7 0 0 1-1.7 1v.3a2 2 0 1 1-4 0v-.3a7 7 0 0 1-1.7-1l-.3.2a2 2 0 1 1-2-3.4l.3-.2a7 7 0 0 1 0-1.8L6 10a2 2 0 1 1 2-3.4l.3.2a7 7 0 0 1 1.7-1v-.3a2 2 0 0 1 2-2Z"/><circle cx="12" cy="11.1" r="2.5"/></svg>';
        const MEMBER_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="m4 8 4 3 4-6 4 6 4-3-1.5 9h-13L4 8Z"/><path d="M6 20h12"/></svg>';
        const ACTIVATION_FALLBACK_CSS = `
    .settings-tool-btn{width:36px;padding:0;justify-content:center}.settings-tool-btn svg{width:17px;height:17px}.settings-tool-btn.member{color:var(--tfa-accent);background:rgba(var(--tfa-accent-rgb),.12)}
    .settings-dialog-shell,.purchase-preview{position:absolute;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;padding:28px;pointer-events:auto}.settings-dialog-overlay,.purchase-preview-backdrop{position:absolute;inset:0;background:rgba(3,6,14,.56);backdrop-filter:blur(8px)}
    .settings-dialog{position:relative;width:min(760px,92vw);height:min(720px,82vh);max-height:min(720px,82vh);display:grid;grid-template-columns:160px minmax(0,1fr);grid-template-rows:minmax(0,1fr);overflow:hidden;border:1px solid var(--tfa-panel-border);border-radius:18px;background:var(--tfa-panel-bg);box-shadow:var(--tfa-panel-shadow)}
    .settings-sidebar{min-height:0;padding:20px 14px;overflow-y:auto;border-right:1px solid var(--tfa-border-soft);background:var(--tfa-hdr-bg)}.settings-nav-item{display:flex;gap:8px;padding:10px;border-radius:10px;color:var(--tfa-text-primary);background:rgba(var(--tfa-accent-rgb),.12)}
    .settings-main{min-width:0;min-height:0;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden}#settingsContent{min-width:0;min-height:0;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;touch-action:pan-y}.settings-dialog-header{display:flex;justify-content:space-between;gap:16px;padding:20px 22px 16px;border-bottom:1px solid var(--tfa-border-soft)}.settings-dialog-close{width:34px;height:34px;border:0;border-radius:10px;background:var(--tfa-btn-bg);color:var(--tfa-text-secondary)}
    .activation-panel{display:grid;gap:14px;padding:20px 22px}.activation-card,.activation-success,.activation-purchase-row{border:1px solid var(--tfa-border-soft);border-radius:14px;background:var(--tfa-card-bg);padding:14px}.activation-purchase-layout{display:grid;grid-template-columns:150px 1fr;gap:16px;align-items:center}.activation-purchase-image{width:100%;max-height:260px;object-fit:contain;border-radius:10px;cursor:zoom-in}.activation-form-row{display:grid;grid-template-columns:1fr auto;gap:10px;margin-top:12px}.activation-input{min-width:0;height:42px;border:1px solid var(--tfa-border-medium);border-radius:10px;background:var(--tfa-input-bg);color:var(--tfa-text-primary);padding:0 12px}.activation-submit{gap:8px}.activation-spinner{width:14px;height:14px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:activation-spin .72s linear infinite}.activation-error{color:var(--tfa-danger);margin-top:8px}.activation-success{display:grid;grid-template-columns:auto 1fr;gap:12px}.activation-success-icon{color:var(--tfa-success)}.activation-success-actions{margin-top:14px;padding-top:12px;border-top:1px solid var(--tfa-border-soft)}.activation-deactivate{padding:0;border:0;background:transparent;color:var(--tfa-danger);font:inherit;font-size:12px;cursor:pointer}
    .deactivation-confirm{position:absolute;inset:0;z-index:95;display:flex;align-items:center;justify-content:center;padding:20px}.deactivation-backdrop{position:absolute;inset:0;background:rgba(3,6,14,.62);backdrop-filter:blur(8px)}.deactivation-dialog{position:relative;width:min(420px,92vw);padding:22px;border:1px solid var(--tfa-panel-border);border-radius:16px;background:var(--tfa-panel-bg);color:var(--tfa-text-primary);box-shadow:var(--tfa-panel-shadow)}.deactivation-close{position:absolute;top:14px;right:14px;width:32px;height:32px;border:0;border-radius:9px;background:var(--tfa-btn-bg);color:var(--tfa-text-secondary)}.deactivation-icon{display:flex;align-items:center;justify-content:center;width:38px;height:38px;margin-bottom:14px;border-radius:50%;background:rgba(var(--tfa-danger-rgb),.14);color:var(--tfa-danger);font-weight:800}.deactivation-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px}.deactivation-danger{gap:8px;background:var(--tfa-danger);color:#fff}.deactivation-error{margin-top:12px;color:var(--tfa-danger);font-size:12px}.deactivation-error[hidden]{display:none}
    .purchase-preview{z-index:90}.purchase-preview-dialog{position:relative;max-width:min(760px,90vw);max-height:88vh;padding:14px;border:1px solid var(--tfa-panel-border);border-radius:18px;background:var(--tfa-panel-bg)}.purchase-preview-image{display:block;max-width:100%;max-height:78vh;object-fit:contain;border-radius:12px}.purchase-preview-close{position:absolute;right:20px;top:20px;width:36px;height:36px;border:1px solid var(--tfa-border-medium);border-radius:10px;background:var(--tfa-panel-bg);color:var(--tfa-text-primary)}
    @keyframes activation-spin{to{transform:rotate(360deg)}}
    @media(max-width:780px){.settings-dialog-shell{padding:12px}.settings-dialog{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr);width:96vw;height:88vh;max-height:88vh}.settings-sidebar{display:flex;flex-direction:row;gap:8px;overflow-x:auto;overflow-y:hidden;padding:10px 12px;border-right:0;border-bottom:1px solid var(--tfa-border-soft)}.settings-nav-item{flex:1 0 auto;justify-content:center}.activation-purchase-layout{grid-template-columns:110px 1fr}.activation-form-row{grid-template-columns:1fr}}
    @media(prefers-reduced-motion:reduce){.activation-spinner{animation:none}.deactivation-confirm,.deactivation-dialog{transition:none!important}}
  `;
        function translated(translate, key, fallback, params = {}) {
          const value = typeof translate === "function" ? translate(key, params) : "";
          const base = value && value !== key ? value : fallback;
          return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
        }
        function localizeOverlay(root, translate) {
          if (!root?.querySelectorAll) return false;
          root.querySelectorAll("[data-i18n]").forEach((node) => {
            node.textContent = translated(translate, node.dataset.i18n, node.dataset.i18nFallback || node.textContent || "");
          });
          for (const [attribute, datasetKey] of [
            ["placeholder", "i18nPlaceholder"],
            ["aria-label", "i18nAriaLabel"],
            ["title", "i18nTitle"]
          ]) {
            root.querySelectorAll(`[data-${datasetKey.replace(/[A-Z]/g, (character) => `-${character.toLowerCase()}`)}]`).forEach((node) => {
              node.setAttribute(attribute, translated(
                translate,
                node.dataset[datasetKey],
                node.dataset[`${datasetKey}Fallback`] || node.getAttribute(attribute) || ""
              ));
            });
          }
          const panel = root.getElementById?.("panel");
          if (panel) panel.dataset.stickyLabel = translated(translate, "overlay.sticky", "\u5DF2\u56FA\u5B9A");
          return true;
        }
        function buildOverlay({ root, version, translate }) {
          const SCRIPT_VERSION = String(version || "");
          const tx = (key, fallback, params) => translated(translate, key, fallback, params);
          root.innerHTML = `
      <style id="tfa-dock-critical">:host([data-styles-ready="false"]) .web-dock-shell{display:none!important}</style>
      <style id="tfa-fallback">
        :host{--tfa-backdrop-bg:rgba(4,8,18,.58);--tfa-panel-bg:rgba(14,18,29,.9);--tfa-panel-border:rgba(255,255,255,.08);--tfa-panel-shadow:0 28px 80px rgba(0,0,0,.42);--tfa-panel-glow:radial-gradient(circle at top,rgba(99,102,241,.12),transparent 38%);--tfa-text-primary:#eef2ff;--tfa-text-secondary:#a8b0c5;--tfa-text-tertiary:#707b91;--tfa-border-soft:rgba(255,255,255,.06);--tfa-border-medium:rgba(255,255,255,.12);--tfa-border-strong:rgba(255,255,255,.18);--tfa-card-bg:rgba(255,255,255,.035);--tfa-card-hover-bg:rgba(255,255,255,.06);--tfa-group-bg:rgba(255,255,255,.03);--tfa-hdr-bg:rgba(255,255,255,.025);--tfa-input-bg:rgba(255,255,255,.045);--tfa-input-focus-bg:rgba(255,255,255,.065);--tfa-btn-bg:rgba(255,255,255,.045);--tfa-btn-hover-bg:rgba(255,255,255,.085);--tfa-accent:#7c8cff;--tfa-accent-2:#5b6df4;--tfa-accent-rgb:124,140,255;--tfa-success:#34d399;--tfa-success-rgb:52,211,153;--tfa-warning:#fbbf24;--tfa-danger:#f87171;--tfa-danger-rgb:248,113,113;--tfa-skeleton-base:rgba(255,255,255,.05);--dock-tile-surface:#1c212b;--dock-accent-contrast:#fff;--tfa-radius-md:12px;--tfa-radius-lg:16px;--tfa-radius-xl:22px;}
        :host([data-theme="light"]){--tfa-backdrop-bg:rgba(220,225,234,.72);--tfa-panel-bg:rgba(255,255,255,.98);--tfa-panel-border:rgba(31,41,55,.12);--tfa-panel-shadow:0 28px 80px rgba(31,41,55,.18);--tfa-panel-glow:radial-gradient(circle at top,rgba(111,98,208,.09),transparent 42%);--tfa-text-primary:#202631;--tfa-text-secondary:#5f6977;--tfa-text-tertiary:#8993a1;--tfa-border-soft:rgba(31,41,55,.09);--tfa-border-medium:rgba(31,41,55,.15);--tfa-border-strong:rgba(31,41,55,.24);--tfa-card-bg:rgba(248,249,251,.98);--tfa-card-hover-bg:#f0f2f5;--tfa-group-bg:rgba(31,41,55,.035);--tfa-hdr-bg:rgba(248,249,251,.96);--tfa-input-bg:#f4f6f8;--tfa-input-focus-bg:#fff;--tfa-btn-bg:#f3f5f7;--tfa-btn-hover-bg:#e7eaf0;--tfa-accent:#6f62d0;--tfa-accent-2:#5f54c4;--tfa-accent-rgb:111,98,208;--tfa-skeleton-base:rgba(31,41,55,.07);--tfa-skeleton-shine:rgba(111,98,208,.15);--tfa-success:#25835f;--tfa-success-rgb:37,131,95;--tfa-danger:#ad4b5a;--tfa-danger-rgb:173,75,90;--tfa-warning:#a66f19;--dock-tile-surface:#fff;--dock-accent-contrast:#fff}
        :host{--tfa-overlay-gutter:clamp(24px,5vw,96px);--tfa-grid-columns:4}
        .overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .22s ease;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Microsoft YaHei','PingFang SC',sans-serif;color:var(--tfa-text-primary)}
        .overlay.show{opacity:1}
        .overlay.show{overscroll-behavior:none}
        .backdrop{position:absolute;inset:0;background:var(--tfa-backdrop-bg);backdrop-filter:blur(18px) saturate(1.18);-webkit-backdrop-filter:blur(18px) saturate(1.18)}
        .panel{position:relative;display:flex;flex-direction:column;width:calc(100vw - var(--tfa-overlay-gutter));height:calc(100vh - var(--tfa-overlay-gutter));max-width:none;max-height:none;background:var(--tfa-panel-bg);border:1px solid var(--tfa-panel-border);border-radius:var(--tfa-radius-xl);box-shadow:var(--tfa-panel-shadow);overflow:hidden;overscroll-behavior:contain;transform:translateY(22px) scale(.97);opacity:0;transition:transform .32s cubic-bezier(.2,.85,.25,1),opacity .24s ease}
        .panel[data-columns="4"]{--tfa-grid-columns:4}.panel[data-columns="5"]{--tfa-grid-columns:5}.panel[data-columns="6"]{--tfa-grid-columns:6}
        .panel::before{content:'';position:absolute;left:0;right:0;top:0;height:180px;background:var(--tfa-panel-glow);pointer-events:none}
        .overlay.show .panel{transform:translateY(0) scale(1);opacity:1}
        .hdr,.body,.ftr{position:relative;z-index:1}
        .hdr{display:flex;flex-direction:column;gap:14px;padding:18px 22px 16px;border-bottom:1px solid var(--tfa-border-soft);background:var(--tfa-hdr-bg)}
        .hdr-top,.hdr-bottom{display:flex;align-items:center;gap:12px}
        .hdr-top{justify-content:space-between}
        .hdr-meta{display:flex;align-items:flex-start;gap:14px;min-width:0;flex:1}
        .hdr-brand{display:flex;flex-direction:column;gap:4px;min-width:0}
        .hdr-kicker{font-size:11px;line-height:1;color:var(--tfa-text-tertiary);text-transform:uppercase;letter-spacing:.12em;font-weight:700}
        .hdr-title-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
        .hdr-title{font-size:18px;line-height:1.2;font-weight:700;color:var(--tfa-text-primary)}
        .hdr-subtitle{font-size:12px;line-height:1.45;color:var(--tfa-text-secondary)}
        .count{display:inline-flex;align-items:center;justify-content:center;min-height:26px;padding:0 10px;border-radius:999px;background:var(--tfa-card-bg);border:1px solid var(--tfa-border-soft);font-size:11.5px;font-weight:700;color:var(--tfa-text-secondary);white-space:nowrap}
        .hdr-tools{display:flex;align-items:center;gap:8px;flex-shrink:0}
        .search-wrap{position:relative;flex:1;min-width:180px}
        .search{width:100%;height:42px;padding:0 38px 0 42px;border-radius:12px;border:1px solid var(--tfa-border-soft);background:var(--tfa-input-bg);color:var(--tfa-text-primary);outline:none;font-size:13.5px}
        .search::placeholder{color:var(--tfa-text-tertiary)}
        .search-icon{position:absolute;left:14px;top:50%;transform:translateY(-50%);width:16px;height:16px;stroke:var(--tfa-text-tertiary);pointer-events:none}
        .search-clear{position:absolute;right:10px;top:50%;transform:translateY(-50%);width:20px;height:20px;border:none;border-radius:999px;background:var(--tfa-btn-bg);color:var(--tfa-text-tertiary);display:flex;align-items:center;justify-content:center;padding:0}
        .actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
        .btn,.close{font-family:inherit}
        .btn{height:36px;padding:0 12px;border-radius:10px;border:1px solid var(--tfa-border-soft);background:var(--tfa-btn-bg);color:var(--tfa-text-primary);font-size:12.5px;display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
        .btn.accent{background:linear-gradient(135deg,var(--tfa-accent),var(--tfa-accent-2));border-color:transparent;color:#fff;font-weight:700}
        .theme-btn{width:36px;padding:0;justify-content:center}
        .close{width:36px;height:36px;border:none;border-radius:10px;background:transparent;color:var(--tfa-text-tertiary);font-size:20px;display:flex;align-items:center;justify-content:center}
        .body{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:24px}
        .ftr{display:flex;flex-wrap:wrap;gap:10px;padding:14px 22px;border-top:1px solid var(--tfa-border-soft);background:var(--tfa-hdr-bg);font-size:11px;color:var(--tfa-text-tertiary);align-items:center}
        .ftr-band{display:flex;align-items:center;gap:8px;min-width:0}
        .ftr-hints{justify-content:center}
        .ftr-meta{justify-content:flex-end}
        .hold-indicator{display:flex;align-items:center;gap:8px}
        .hold-bar{width:56px;height:4px;border-radius:999px;background:var(--tfa-border-medium);overflow:hidden}
        .hold-bar .fill{height:100%;width:0;background:linear-gradient(90deg,var(--tfa-accent),var(--tfa-accent-2))}
        .sticky-mode .panel::after{content:attr(data-sticky-label);position:absolute;top:16px;right:68px;padding:4px 8px;border-radius:999px;background:rgba(var(--tfa-accent-rgb),.12);border:1px solid rgba(var(--tfa-accent-rgb),.22);font-size:10px;font-weight:700;color:var(--tfa-accent)}
        .toast-container{position:fixed;left:50%;top:20px;transform:translateX(-50%);z-index:140;display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none}
        .toast{display:flex;align-items:center;gap:8px;min-height:42px;padding:0 14px;background:var(--tfa-panel-bg);border:1px solid var(--tfa-panel-border);border-radius:14px;box-shadow:0 12px 36px rgba(0,0,0,.28);opacity:0;transform:translateY(16px) scale(.96);transition:opacity .2s ease,transform .2s cubic-bezier(.2,.85,.25,1);pointer-events:auto;white-space:nowrap;font-size:12.5px;font-weight:600}
        .toast.show{opacity:1;transform:translateY(0) scale(1)}
        .toast.hide{opacity:0;transform:translateY(-8px) scale(.96)}
        .toast-icon{font-size:14px;line-height:1}
        .empty{text-align:center;padding:84px 20px;color:var(--tfa-text-secondary);font-size:14px;display:flex;flex-direction:column;align-items:center;gap:6px}
        .empty .big{font-size:40px;opacity:.34}
        .empty-hint{font-size:12px;color:var(--tfa-text-tertiary)}
        kbd{background:var(--tfa-btn-bg);border:1px solid var(--tfa-border-soft);border-bottom-width:2px;border-radius:5px;padding:1px 5px;font-size:10px;font-weight:700;color:var(--tfa-text-secondary)}
        .tab-preview{position:absolute;width:380px;z-index:50;pointer-events:auto;opacity:0;transform:translateY(8px) scale(.96);transition:opacity .2s ease,transform .2s cubic-bezier(.2,.85,.25,1)}
        .tab-preview.show{opacity:1;transform:translateY(0) scale(1)}
        .preview-inner{background:var(--tfa-panel-bg);border:1px solid var(--tfa-panel-border);border-radius:16px;box-shadow:0 24px 64px rgba(0,0,0,.38);overflow:hidden}
        .preview-header{display:flex;align-items:center;gap:10px;padding:12px 14px 10px;border-bottom:1px solid var(--tfa-border-soft)}
        .preview-header-text{flex:1;min-width:0}.preview-favicon,.preview-favicon-fallback{width:20px;height:20px;border-radius:5px;flex-shrink:0}.preview-favicon{object-fit:contain;background:var(--tfa-skeleton-base)}.preview-favicon-fallback{display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;color:#fff}.preview-title{font-size:13px;font-weight:700;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.preview-url{font-size:11px;color:var(--tfa-text-tertiary);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.preview-snapshot{width:100%;height:200px;background:rgba(6,10,20,.42);overflow:hidden;position:relative}.preview-screenshot{width:100%;height:100%;object-fit:contain;object-position:top center;display:block}.preview-snapshot.placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;background:linear-gradient(135deg,var(--dc,#4a4a6a),rgba(0,0,0,.52))}.preview-letter{font-size:46px;font-weight:800;color:rgba(255,255,255,.92)}.preview-domain{font-size:13px;color:rgba(255,255,255,.72);font-weight:600}.preview-loading{font-size:11px;color:rgba(255,255,255,.54)}.preview-footer{display:flex;flex-wrap:wrap;gap:6px;padding:10px 14px 12px}.preview-tag{font-size:10px;padding:2px 8px;border-radius:999px;background:var(--tfa-card-bg);color:var(--tfa-text-secondary);font-weight:700}
        .grid,.group-cards,.skeleton-grid{display:grid;grid-template-columns:repeat(var(--tfa-grid-columns),minmax(0,1fr));gap:11px}.card-title{display:block;min-height:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.card-tags:empty{display:none}.layout-controls,.density-control,.view-segment,.preview-toggle{display:flex;align-items:center;gap:8px}.layout-controls{margin-left:auto}.view-segment{border:1px solid var(--tfa-border-medium);border-radius:9px;overflow:hidden}.view-segment-btn{height:30px;padding:0 10px;border:0;background:var(--tfa-btn-bg);color:var(--tfa-text-secondary)}.view-segment-btn.active{color:var(--tfa-accent);background:rgba(var(--tfa-accent-rgb),.14)}.preview-toggle[hidden]{display:none}.card-preview-media{display:none}.panel[data-view-mode="card"] .card-preview-media{display:flex;aspect-ratio:16/9;width:100%;overflow:hidden;align-items:center;justify-content:center}.card-preview-screenshot{width:100%;height:100%;object-fit:cover;object-position:top center}.panel[data-view-mode="card"] .card{padding:0}.panel[data-view-mode="card"] .card-main{padding:11px 13px}
        @media (max-width:1199px){.panel[data-columns="6"]{--tfa-grid-columns:5}}@media (max-width:999px){.panel[data-columns="5"],.panel[data-columns="6"]{--tfa-grid-columns:4}}@media (max-width:839px){.panel[data-columns="4"],.panel[data-columns="5"],.panel[data-columns="6"]{--tfa-grid-columns:3}}@media (max-width:679px){.panel[data-columns="4"],.panel[data-columns="5"],.panel[data-columns="6"]{--tfa-grid-columns:2}.hdr-top,.hdr-bottom{flex-wrap:wrap;align-items:stretch}.hdr-tools,.actions{width:100%}.layout-controls{width:100%;margin-left:0;flex-wrap:wrap}.ftr-hints,.ftr-meta{justify-content:flex-start}.tab-preview{width:min(320px,calc(100vw - 32px))}}@media (max-width:479px){.panel[data-columns="4"],.panel[data-columns="5"],.panel[data-columns="6"]{--tfa-grid-columns:1}}
      </style>
      <style id="tfa-activation-fallback">${ACTIVATION_FALLBACK_CSS}</style>
      <div class="overlay" id="overlay">
        <div class="backdrop" id="backdrop"></div>
        <div class="panel" id="panel" data-columns="4" data-view-mode="hover" data-hover-preview="true" data-sticky-label="${tx("overlay.sticky", "\u5DF2\u56FA\u5B9A")}">
          <header class="hdr">
            <div class="hdr-top">
              <div class="hdr-meta">
                <div class="hdr-brand">
                  <div class="hdr-kicker">Workspace</div>
                  <div class="hdr-title-row">
                    <div class="hdr-title" data-i18n="overlay.title" data-i18n-fallback="\u9875\u7B7E\u5DE5\u4F5C\u53F0">${tx("overlay.title", "\u9875\u7B7E\u5DE5\u4F5C\u53F0")}</div>
                    <div class="count" id="count"></div>
                  </div>
                  <div class="hdr-subtitle" data-i18n="overlay.subtitle" data-i18n-fallback="\u641C\u7D22\u3001\u6574\u7406\u3001\u5206\u7EC4\u3001\u6062\u590D\u4E0E\u9884\u89C8\u90FD\u96C6\u4E2D\u5728\u8FD9\u91CC">${tx("overlay.subtitle", "\u641C\u7D22\u3001\u6574\u7406\u3001\u5206\u7EC4\u3001\u6062\u590D\u4E0E\u9884\u89C8\u90FD\u96C6\u4E2D\u5728\u8FD9\u91CC")}</div>
                </div>
              </div>
              <div class="hdr-tools">
                <button class="btn theme-btn" id="btnTheme" title="${tx("common.theme", "\u5207\u6362\u76AE\u80A4")}" data-i18n-title="common.theme" data-i18n-title-fallback="\u5207\u6362\u76AE\u80A4"><span class="ico">&#x1F3A8;</span></button>
                <button class="btn settings-tool-btn" id="btnSettings" aria-label="${tx("common.settings", "\u6253\u5F00\u8BBE\u7F6E")}" title="${tx("common.settings", "\u6253\u5F00\u8BBE\u7F6E")}" data-i18n-aria-label="common.settings" data-i18n-aria-label-fallback="\u6253\u5F00\u8BBE\u7F6E" data-i18n-title="common.settings" data-i18n-title-fallback="\u6253\u5F00\u8BBE\u7F6E">${SETTINGS_SVG}</button>
                <button class="btn settings-tool-btn member" id="btnMember" aria-label="${tx("overlay.membership", "\u4F1A\u5458\u72B6\u6001")}" title="${tx("overlay.activated", "\u5DF2\u6C38\u4E45\u6FC0\u6D3B")}" style="display:none;" data-i18n-aria-label="overlay.membership" data-i18n-aria-label-fallback="\u4F1A\u5458\u72B6\u6001" data-i18n-title="overlay.activated" data-i18n-title-fallback="\u5DF2\u6C38\u4E45\u6FC0\u6D3B">${MEMBER_SVG}</button>
                <button class="close" id="btnClose" title="${tx("overlay.close", "\u5173\u95ED\u6D6E\u5C42 (Esc)")}" data-i18n-title="overlay.close" data-i18n-title-fallback="\u5173\u95ED\u6D6E\u5C42 (Esc)">\xD7</button>
              </div>
            </div>
            <div class="hdr-bottom">
              <div class="search-wrap">
                ${SEARCH_SVG}
                <input id="search" class="search" placeholder="${tx("overlay.searchPlaceholder", "\u641C\u7D22\u9875\u7B7E\u6807\u9898\u3001\u57DF\u540D\u6216\u7F51\u5740")}" data-i18n-placeholder="overlay.searchPlaceholder" data-i18n-placeholder-fallback="\u641C\u7D22\u9875\u7B7E\u6807\u9898\u3001\u57DF\u540D\u6216\u7F51\u5740" autocomplete="off" spellcheck="false" />
                <button class="search-clear" id="searchClear" style="display:none;">&#x2715;</button>
              </div>
              <div class="actions">
                <button class="btn accent" id="btnNew" title="${tx("overlay.action.newTitle", "\u65B0\u5EFA\u9875\u7B7E\uFF08\u70ED\u95E8\u4E66\u7B7E\uFF09")}" data-i18n-title="overlay.action.newTitle" data-i18n-title-fallback="\u65B0\u5EFA\u9875\u7B7E\uFF08\u70ED\u95E8\u4E66\u7B7E\uFF09"><span class="ico">\uFF0B</span><span data-i18n="overlay.action.new" data-i18n-fallback="\u65B0\u5EFA">${tx("overlay.action.new", "\u65B0\u5EFA")}</span></button>
                <button class="btn" id="btnDomainGroup" title="${tx("overlay.action.groupDomainsTitle", "\u6309\u4E3B\u57DF\u540D\u4E00\u952E\u5206\u7EC4\u6240\u6709\u9875\u7B7E")}" data-i18n-title="overlay.action.groupDomainsTitle" data-i18n-title-fallback="\u6309\u4E3B\u57DF\u540D\u4E00\u952E\u5206\u7EC4\u6240\u6709\u9875\u7B7E"><span class="ico">&#x1F310;</span><span data-i18n="overlay.action.groupDomains" data-i18n-fallback="\u57DF\u540D\u5206\u7EC4">${tx("overlay.action.groupDomains", "\u57DF\u540D\u5206\u7EC4")}</span></button>
                <button class="btn" id="btnUngroupAll" title="${tx("overlay.action.ungroupAllTitle", "\u89E3\u6563\u6240\u6709\u5206\u7EC4")}" data-i18n-title="overlay.action.ungroupAllTitle" data-i18n-title-fallback="\u89E3\u6563\u6240\u6709\u5206\u7EC4"><span class="ico">&#x26D3;</span><span data-i18n="overlay.action.ungroupAll" data-i18n-fallback="\u89E3\u7EC4">${tx("overlay.action.ungroupAll", "\u89E3\u7EC4")}</span></button>
                <button class="btn" id="btnTrash" title="${tx("overlay.action.recycleTitle", "\u67E5\u770B\u56DE\u6536\u7AD9")}" data-i18n-title="overlay.action.recycleTitle" data-i18n-title-fallback="\u67E5\u770B\u56DE\u6536\u7AD9"><span class="ico">&#x267B;</span><span data-i18n="overlay.action.recycle" data-i18n-fallback="\u56DE\u6536\u7AD9">${tx("overlay.action.recycle", "\u56DE\u6536\u7AD9")}</span></button>
                <button class="btn" id="btnUndo" title="${tx("overlay.action.undoTitle", "\u5173\u95ED\u56DE\u64A4\uFF08\u6062\u590D\u6700\u8FD1\u4E00\u4E2A\uFF09")}" data-i18n-title="overlay.action.undoTitle" data-i18n-title-fallback="\u5173\u95ED\u56DE\u64A4\uFF08\u6062\u590D\u6700\u8FD1\u4E00\u4E2A\uFF09"><span class="ico">\u21B6</span><span data-i18n="overlay.action.undo" data-i18n-fallback="\u56DE\u64A4">${tx("overlay.action.undo", "\u56DE\u64A4")}</span></button>
              </div>
            </div>
          </header>
          <div class="body" id="body"></div>
          <footer class="ftr" id="footer">
            <div class="ftr-band">
              <span><kbd>Esc</kbd> <span data-i18n="overlay.footer.escapeClose" data-i18n-fallback="\u5173\u95ED">${tx("overlay.footer.escapeClose", "\u5173\u95ED")}</span></span>
              <span data-i18n="overlay.footer.holdOpen" data-i18n-fallback="\u6309\u4F4F\u5FEB\u6377\u952E\u5524\u8D77">${tx("overlay.footer.holdOpen", "\u6309\u4F4F\u5FEB\u6377\u952E\u5524\u8D77")}</span>
              <span><span data-i18n="overlay.footer.stickyBefore" data-i18n-fallback="\u8D85\u8FC7">${tx("overlay.footer.stickyBefore", "\u8D85\u8FC7")}</span> <span id="holdSec">2</span> <span data-i18n="overlay.footer.stickyAfter" data-i18n-fallback="\u79D2\u677E\u5F00\u4FDD\u6301">${tx("overlay.footer.stickyAfter", "\u79D2\u677E\u5F00\u4FDD\u6301")}</span></span>
            </div>
            <div class="ftr-band hold-indicator" id="holdWrap" style="display:none;">
              <span data-i18n="overlay.footer.holdDuration" data-i18n-fallback="\u6309\u4F4F\u65F6\u957F">${tx("overlay.footer.holdDuration", "\u6309\u4F4F\u65F6\u957F")}</span>
              <div class="hold-bar"><div class="fill" id="holdFill"></div></div>
            </div>
            <div class="ftr-band ftr-hints" data-i18n="overlay.footer.hints" data-i18n-fallback="\u70B9\u51FB\u5207\u6362 \xB7 \u60AC\u505C\u9884\u89C8 \xB7 \u62D6\u52A8\u6392\u5E8F \xB7 \u53F3\u952E\u591A\u9009">${tx("overlay.footer.hints", "\u70B9\u51FB\u5207\u6362 \xB7 \u60AC\u505C\u9884\u89C8 \xB7 \u62D6\u52A8\u6392\u5E8F \xB7 \u53F3\u952E\u591A\u9009")}</div>
            <div class="layout-controls" id="layoutControls" aria-label="${tx("overlay.layout.aria", "\u9875\u7B7E\u5E03\u5C40\u8BBE\u7F6E")}" data-i18n-aria-label="overlay.layout.aria" data-i18n-aria-label-fallback="\u9875\u7B7E\u5E03\u5C40\u8BBE\u7F6E">
              <label class="density-control" for="columnRange">
                <span class="control-label" data-i18n="overlay.layout.perRow" data-i18n-fallback="\u6BCF\u884C">${tx("overlay.layout.perRow", "\u6BCF\u884C")}</span>
                <input id="columnRange" class="density-range" type="range" min="4" max="6" step="1" value="4" aria-label="${tx("overlay.layout.perRowAria", "\u6BCF\u884C\u9875\u7B7E\u6570\u91CF")}" data-i18n-aria-label="overlay.layout.perRowAria" data-i18n-aria-label-fallback="\u6BCF\u884C\u9875\u7B7E\u6570\u91CF" />
                <output id="columnValue" class="density-value" for="columnRange">${tx("overlay.layout.columns", "4\u5217", { count: 4 })}</output>
              </label>
              <div class="view-segment" role="group" aria-label="${tx("overlay.layout.viewAria", "\u9875\u7B7E\u89C6\u56FE")}" data-i18n-aria-label="overlay.layout.viewAria" data-i18n-aria-label-fallback="\u9875\u7B7E\u89C6\u56FE">
                <button class="view-segment-btn active" type="button" data-view-mode="hover" aria-pressed="true" data-i18n="overlay.layout.hover" data-i18n-fallback="\u60AC\u6D6E">${tx("overlay.layout.hover", "\u60AC\u6D6E")}</button>
                <button class="view-segment-btn" type="button" data-view-mode="card" aria-pressed="false" data-i18n="overlay.layout.card" data-i18n-fallback="\u5361\u7247">${tx("overlay.layout.card", "\u5361\u7247")}</button>
              </div>
              <button class="preview-toggle" id="hoverPreviewToggle" type="button" role="switch" aria-checked="true">
                <span class="toggle-track" aria-hidden="true"><span class="toggle-thumb"></span></span>
                <span data-i18n="overlay.layout.hoverPreview" data-i18n-fallback="\u60AC\u505C\u9884\u89C8">${tx("overlay.layout.hoverPreview", "\u60AC\u505C\u9884\u89C8")}</span>
              </button>
            </div>
            <div class="ftr-band ftr-meta"><span id="ftrVersion">v${SCRIPT_VERSION} \xB7 <span id="ftrTheme">${tx("theme.system", "\u8DDF\u968F\u7CFB\u7EDF")}</span></span></div>
          </footer>
        </div>
        <div class="select-bar" id="selectBar">
          <span class="sel-count" id="selCount" data-i18n="overlay.selectedNone" data-i18n-fallback="\u5DF2\u9009 0">${tx("overlay.selectedNone", "\u5DF2\u9009 0")}</span>
          <button class="btn accent" id="btnGroup" title="${tx("overlay.selection.groupTitle", "\u5C06\u9009\u4E2D\u9875\u7B7E\u5F52\u5165\u65B0\u5206\u7EC4")}" data-i18n-title="overlay.selection.groupTitle" data-i18n-title-fallback="\u5C06\u9009\u4E2D\u9875\u7B7E\u5F52\u5165\u65B0\u5206\u7EC4" data-i18n="overlay.selection.group" data-i18n-fallback="\u5206\u7EC4\u9009\u4E2D">${tx("overlay.selection.group", "\u5206\u7EC4\u9009\u4E2D")}</button>
          <button class="btn" id="btnCloseSelected" title="${tx("overlay.selection.closeTitle", "\u6279\u91CF\u5173\u95ED\u9009\u4E2D")}" data-i18n-title="overlay.selection.closeTitle" data-i18n-title-fallback="\u6279\u91CF\u5173\u95ED\u9009\u4E2D" data-i18n="overlay.selection.close" data-i18n-fallback="\u6279\u91CF\u5173\u95ED">${tx("overlay.selection.close", "\u6279\u91CF\u5173\u95ED")}</button>
          <button class="btn" id="btnClearSel" title="${tx("overlay.selection.clearTitle", "\u53D6\u6D88\u9009\u62E9")}" data-i18n-title="overlay.selection.clearTitle" data-i18n-title-fallback="\u53D6\u6D88\u9009\u62E9" data-i18n="overlay.selection.clear" data-i18n-fallback="\u53D6\u6D88">${tx("overlay.selection.clear", "\u53D6\u6D88")}</button>
        </div>
        <div class="tab-preview" id="tabPreview" style="display:none"></div>
      </div>
      <div class="toast-container" id="toastContainer" aria-live="polite" aria-atomic="true"></div>
      <div class="web-dock-shell" id="webDockShell" hidden data-auto-hide="true" data-expanded="false" data-suspended="false">
        <button class="web-dock-trigger" id="webDockTrigger" type="button" aria-label="${tx("dock.expand", "\u5C55\u5F00\u7F51\u9875\u62D3\u5C55\u575E")}" data-i18n-aria-label="dock.expand" data-i18n-aria-label-fallback="\u5C55\u5F00\u7F51\u9875\u62D3\u5C55\u575E"></button>
        <nav class="web-dock" aria-label="${tx("dock.label", "\u7F51\u9875\u62D3\u5C55\u575E")}" data-i18n-aria-label="dock.label" data-i18n-aria-label-fallback="\u7F51\u9875\u62D3\u5C55\u575E">
          <button class="web-dock-control web-dock-add-control" id="webDockAdd" type="button" aria-label="${tx("dock.addSite", "\u65B0\u589E\u7F51\u7AD9")}" data-i18n-aria-label="dock.addSite" data-i18n-aria-label-fallback="\u65B0\u589E\u7F51\u7AD9">
            <span class="web-dock-control-icon" aria-hidden="true" data-icon="tabler:circle-plus-2"><svg viewBox="0 0 24 24"><path d="M20.985 12.522a9 9 0 1 0 -8.475 8.464"/><path d="M16 19h6"/><path d="M19 16v6"/></svg></span>
            <span class="web-dock-control-label" data-i18n="dock.addSite" data-i18n-fallback="\u65B0\u589E\u7F51\u7AD9">${tx("dock.addSite", "\u65B0\u589E\u7F51\u7AD9")}</span>
          </button>
          <button class="web-dock-control web-dock-search-control" id="webDockSearch" type="button" aria-label="${tx("dock.search", "\u7F51\u9875\u641C\u7D22")}" data-i18n-aria-label="dock.search" data-i18n-aria-label-fallback="\u7F51\u9875\u641C\u7D22">
            <span class="web-dock-control-icon" aria-hidden="true" data-icon="tabler:search"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4 -4"/></svg></span>
            <span class="web-dock-control-label" data-i18n="dock.search" data-i18n-fallback="\u7F51\u9875\u641C\u7D22">${tx("dock.search", "\u7F51\u9875\u641C\u7D22")}</span>
          </button>
          <div class="web-dock-track" id="webDockTrack"></div>
          <button class="web-dock-control web-dock-settings-control" id="webDockSettings" type="button" aria-label="${tx("dock.settings", "\u7F51\u9875\u62D3\u5C55\u575E\u8BBE\u7F6E")}" data-i18n-aria-label="dock.settings" data-i18n-aria-label-fallback="\u7F51\u9875\u62D3\u5C55\u575E\u8BBE\u7F6E">
            <span class="web-dock-control-icon" aria-hidden="true" data-icon="tabler:settings"><svg viewBox="0 0 24 24"><path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c.996 .608 2.296 .07 2.572 -1.065z"/><path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0"/></svg></span>
            <span class="web-dock-control-label" data-i18n="dock.settingsShort" data-i18n-fallback="\u62D3\u5C55\u575E\u8BBE\u7F6E">${tx("dock.settingsShort", "\u62D3\u5C55\u575E\u8BBE\u7F6E")}</span>
          </button>
          <button class="web-dock-control web-dock-recent-control" id="webDockRecent" type="button" aria-label="${tx("dock.recent", "\u6700\u8FD1\u5173\u95ED")}" data-i18n-aria-label="dock.recent" data-i18n-aria-label-fallback="\u6700\u8FD1\u5173\u95ED" aria-expanded="false" aria-controls="webDockRecentPanel">
            <span class="web-dock-control-icon" aria-hidden="true" data-icon="tabler:trash"><svg viewBox="0 0 24 24"><path d="M4 7h16"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M5 7l1 14h12l1-14"/><path d="M9 7V4h6v3"/></svg></span>
            <span class="web-dock-control-label" data-i18n="dock.recent" data-i18n-fallback="\u6700\u8FD1\u5173\u95ED">${tx("dock.recent", "\u6700\u8FD1\u5173\u95ED")}</span>
          </button>
        </nav>
        <section class="web-dock-recent-panel" id="webDockRecentPanel" role="region" aria-label="${tx("dock.recentSites", "\u6700\u8FD1\u5173\u95ED\u7684\u7F51\u7AD9")}" data-i18n-aria-label="dock.recentSites" data-i18n-aria-label-fallback="\u6700\u8FD1\u5173\u95ED\u7684\u7F51\u7AD9" data-columns="5" data-visible-rows="3" hidden>
          <header class="web-dock-recent-header"><strong data-i18n="dock.recent" data-i18n-fallback="\u6700\u8FD1\u5173\u95ED">${tx("dock.recent", "\u6700\u8FD1\u5173\u95ED")}</strong><span id="webDockRecentCount">0</span></header>
          <div class="web-dock-recent-status" id="webDockRecentStatus" role="status" data-i18n="dock.recentLoading" data-i18n-fallback="\u6B63\u5728\u8BFB\u53D6\u6700\u8FD1\u5173\u95ED\u2026">${tx("dock.recentLoading", "\u6B63\u5728\u8BFB\u53D6\u6700\u8FD1\u5173\u95ED\u2026")}</div>
          <div class="web-dock-recent-list" id="webDockRecentList" hidden></div>
        </section>
      </div>
    `;
        }
        return Object.freeze({ buildOverlay, localizeOverlay });
      });
    }
  });

  // src/content/overlay-runtime.js
  var require_overlay_runtime = __commonJS({
    "src/content/overlay-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaOverlayRuntime = api;
        else if (!commonJs) root.TfaOverlayRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createOverlayRuntime(deps = {}) {
          const document2 = deps.document;
          const window2 = deps.window;
          const hostId = deps.hostId || "tfa-host";
          const owner = deps.owner || "tfa";
          const instanceId = deps.instanceId || "";
          let destroyed = false;
          let visible = false;
          let sticky = false;
          let host = null;
          let root = null;
          let elements = {};
          let cssPromise = null;
          let cancelCssWait = null;
          let cssEpoch = 0;
          let openEpoch = 0;
          let scrollState = null;
          function ownedHost() {
            const candidate = document2?.getElementById?.(hostId);
            const matchesIdentity = (node) => node?.getAttribute?.("data-tfa-owner") === owner && (!instanceId || node?.getAttribute?.("data-tfa-instance") === instanceId);
            if (matchesIdentity(candidate)) return candidate;
            const candidates = document2?.documentElement?.querySelectorAll?.('[data-tfa-host="true"]') || [];
            for (const ownedCandidate of candidates) {
              if (ownedCandidate?.id === hostId && matchesIdentity(ownedCandidate)) return ownedCandidate;
            }
            return null;
          }
          function ensureHost() {
            if (destroyed) return null;
            const existing = ownedHost();
            const complete = typeof deps.isReusableHost === "function" ? deps.isReusableHost(existing) : existing?.isConnected && existing.shadowRoot && (!instanceId || existing.getAttribute("data-tfa-instance") === instanceId) && (deps.requiredIds || []).every((id) => existing.shadowRoot.getElementById(id));
            if (complete) {
              if (host === existing && root === existing.shadowRoot) return host;
              if (host && host !== existing) resetCss();
              host = existing;
              root = existing.shadowRoot;
              elements = deps.collectElements?.(root) || elements;
              deps.onHost?.({ host, root, elements, reused: true });
              return host;
            }
            if (existing) existing.remove();
            resetCss();
            host = document2.createElement("div");
            host.id = hostId;
            host.setAttribute("data-tfa-host", "true");
            host.setAttribute("data-tfa-owner", owner);
            if (instanceId) host.setAttribute("data-tfa-instance", instanceId);
            host.setAttribute("data-styles-ready", "false");
            if (deps.hostStyle) host.style.cssText = deps.hostStyle;
            root = host.attachShadow({ mode: "open" });
            const built = deps.buildOverlay?.(root) || {};
            elements = built.elements || deps.collectElements?.(root) || {};
            document2.documentElement.appendChild(host);
            deps.onHost?.({ host, root, elements });
            return host;
          }
          function getContext() {
            return Object.freeze({ host, root, elements, visible, sticky, destroyed });
          }
          function quarantineElements(targetElements) {
            const overlay = targetElements?.overlay;
            overlay?.classList?.remove("show", "sticky-mode");
            if (overlay) {
              overlay.hidden = true;
              overlay.inert = true;
              overlay.setAttribute?.("aria-hidden", "true");
              if (overlay.style) overlay.style.pointerEvents = "none";
            }
            const dock = targetElements?.webDockShell;
            if (dock) {
              dock.hidden = true;
              dock.inert = true;
              dock.setAttribute?.("aria-hidden", "true");
              if (dock.style) dock.style.pointerEvents = "none";
            }
          }
          function recoverHost() {
            if (destroyed) return false;
            const owned = host?.getAttribute?.("data-tfa-owner") === owner;
            const sameInstance = !instanceId || host?.getAttribute?.("data-tfa-instance") === instanceId;
            const reusable = typeof deps.isReusableHost === "function" ? deps.isReusableHost(host) : host?.isConnected && host.shadowRoot && (deps.requiredIds || []).every((id) => host.shadowRoot.getElementById(id));
            if (owned && sameInstance && reusable) return false;
            const previousHost = host;
            if (previousHost && (!owned || !sameInstance)) quarantineElements(elements);
            deps.onBeforeRecoverHost?.({ host, root, elements, visible, sticky });
            resetCss();
            if (owned && sameInstance) previousHost?.remove?.();
            host = null;
            root = null;
            elements = {};
            const replacement = ensureHost();
            if (!replacement) return false;
            preloadCss();
            if (visible) {
              elements.overlay?.classList?.add("show");
              elements.overlay?.classList?.toggle("sticky-mode", sticky);
              deps.updateDockSuspension?.(true);
            }
            deps.onRecoverHost?.({ previousHost, host, root, elements, visible, sticky });
            return true;
          }
          function preloadCss() {
            if (destroyed) return Promise.resolve(false);
            if (!host) ensureHost();
            if (!host?.isConnected) return Promise.resolve(false);
            if (cssPromise) return cssPromise;
            const targetHost = host;
            const targetRoot = root;
            const epoch = ++cssEpoch;
            const invalidated = new Promise((resolve) => {
              cancelCssWait = () => resolve(false);
            });
            const work = Promise.resolve(deps.fetchStyles?.()).then((css) => {
              if (destroyed || epoch !== cssEpoch || targetHost !== host || targetRoot !== root || !targetHost?.isConnected) return false;
              const text = typeof css === "string" ? css.trim() : "";
              if (!text) return false;
              let style = targetRoot.getElementById?.("tfa-full-styles");
              if (!style) {
                style = document2.createElement("style");
                style.id = "tfa-full-styles";
                if (targetRoot.insertBefore) targetRoot.insertBefore(style, targetRoot.firstChild);
                else targetRoot.prepend(style);
              }
              style.textContent = text;
              targetHost.setAttribute("data-styles-ready", "true");
              deps.onCssReady?.({ host: targetHost, root: targetRoot, elements });
              return true;
            }).catch(() => false);
            cssPromise = Promise.race([work, invalidated]);
            return cssPromise;
          }
          function resetCss() {
            if (destroyed) return false;
            cssEpoch += 1;
            cancelCssWait?.();
            cancelCssWait = null;
            cssPromise = null;
            return true;
          }
          function lockScroll() {
            if (destroyed || scrollState || !document2?.documentElement || !document2?.body) return false;
            const html = document2.documentElement.style;
            const body = document2.body.style;
            scrollState = {
              htmlOverflow: html.overflow,
              bodyOverflow: body.overflow,
              bodyOverscroll: body.overscrollBehavior
            };
            html.overflow = "hidden";
            body.overflow = "hidden";
            body.overscrollBehavior = "none";
            return true;
          }
          function unlockScroll() {
            if (!scrollState || !document2?.documentElement || !document2?.body) return false;
            const html = document2.documentElement.style;
            const body = document2.body.style;
            html.overflow = scrollState.htmlOverflow;
            body.overflow = scrollState.bodyOverflow;
            body.overscrollBehavior = scrollState.bodyOverscroll;
            scrollState = null;
            return true;
          }
          async function show(options = {}) {
            if (destroyed) return false;
            const epoch = ++openEpoch;
            ensureHost();
            visible = true;
            sticky = options.sticky === true;
            lockScroll();
            elements.overlay?.classList?.add("show");
            elements.overlay?.classList?.toggle("sticky-mode", sticky);
            deps.updateDockSuspension?.(true);
            deps.onEnter?.({ sticky, host, root, elements });
            const dataWork = options.loadData === false ? Promise.resolve(void 0) : Promise.resolve(deps.loadData?.(options));
            const [, data] = await Promise.all([preloadCss(), dataWork]);
            if (destroyed || epoch !== openEpoch || !visible) return false;
            if (options.loadData !== false) deps.applyData?.(data, options);
            return true;
          }
          async function refresh(options = {}) {
            if (destroyed || !visible) return false;
            const epoch = openEpoch;
            const [, data] = await Promise.all([preloadCss(), Promise.resolve(deps.loadData?.(options))]);
            if (destroyed || epoch !== openEpoch || !visible) return false;
            deps.applyData?.(data, options);
            return true;
          }
          function hide() {
            if (destroyed) return false;
            openEpoch += 1;
            const wasVisible = visible;
            visible = false;
            sticky = false;
            elements.overlay?.classList?.remove("show", "sticky-mode");
            unlockScroll();
            deps.updateDockSuspension?.(false);
            if (wasVisible) deps.onExit?.();
            return wasVisible;
          }
          function setSticky(value) {
            if (destroyed || !visible) return false;
            sticky = value === true;
            elements.overlay?.classList?.toggle("sticky-mode", sticky);
            return true;
          }
          function toggle(options = {}) {
            if (destroyed) return false;
            if (visible) {
              hide();
              return false;
            }
            show(options);
            return true;
          }
          function beginOpen() {
            if (destroyed) return -1;
            return ++openEpoch;
          }
          function isOpenCurrent(epoch) {
            return !destroyed && epoch === openEpoch;
          }
          function invalidateOpen() {
            if (destroyed) return false;
            openEpoch += 1;
            return true;
          }
          function destroy() {
            if (destroyed) return false;
            const wasVisible = visible;
            openEpoch += 1;
            cssEpoch += 1;
            visible = false;
            sticky = false;
            unlockScroll();
            deps.updateDockSuspension?.(false);
            if (wasVisible) deps.onExit?.({ destroying: true });
            const owned = host?.getAttribute?.("data-tfa-owner") === owner;
            const sameInstance = !instanceId || host?.getAttribute?.("data-tfa-instance") === instanceId;
            if (owned && sameInstance) host.remove?.();
            host = null;
            root = null;
            elements = {};
            cancelCssWait?.();
            cssPromise = null;
            cancelCssWait = null;
            destroyed = true;
            return true;
          }
          function inspect() {
            return {
              destroyed,
              visible,
              sticky,
              openEpoch,
              cssEpoch,
              scrollLocked: Boolean(scrollState),
              hostConnected: Boolean(host?.isConnected)
            };
          }
          function getCssPromiseForTests() {
            return cssPromise;
          }
          return Object.freeze({
            ensureHost,
            getContext,
            recoverHost,
            preloadCss,
            resetCss,
            lockScroll,
            unlockScroll,
            show,
            refresh,
            hide,
            setSticky,
            toggle,
            beginOpen,
            isOpenCurrent,
            invalidateOpen,
            destroy,
            inspect,
            getCssPromiseForTests
          });
        }
        return { createOverlayRuntime };
      });
    }
  });

  // src/content/theme-runtime.js
  var require_theme_runtime = __commonJS({
    "src/content/theme-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaThemeRuntime = api;
        else if (!commonJs && root) root.TfaThemeRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        const THEME_STORAGE_KEY = "tfa_theme";
        const LEGACY_LIGHT_THEMES = /* @__PURE__ */ new Set(["snow", "paper", "pro"]);
        const LEGACY_DARK_THEMES = /* @__PURE__ */ new Set(["noir", "ocean", "space", "crypto"]);
        const THEMES = Object.freeze([
          Object.freeze({ id: "system", icon: "&#x25D0;", labelKey: "theme.system", fallback: "\u8DDF\u968F\u7CFB\u7EDF" }),
          Object.freeze({ id: "light", icon: "&#x2600;", labelKey: "theme.light", fallback: "\u6D45\u8272" }),
          Object.freeze({ id: "dark", icon: "&#x263E;", labelKey: "theme.dark", fallback: "\u6DF1\u8272" })
        ]);
        const THEME_COLORS = Object.freeze({
          light: Object.freeze({
            backdropBg: "rgba(220,225,234,.72)",
            panelBg: "rgba(255,255,255,.98)",
            panelBorder: "rgba(31,41,55,.12)",
            panelShadow: "0 28px 80px rgba(31,41,55,.18)",
            panelGlow: "radial-gradient(circle at top,rgba(111,98,208,.09),transparent 42%)",
            textPrimary: "#202631",
            textSecondary: "#5f6977",
            textTertiary: "#8993a1",
            borderSoft: "rgba(31,41,55,.09)",
            borderMedium: "rgba(31,41,55,.15)",
            borderStrong: "rgba(31,41,55,.24)",
            cardBg: "rgba(248,249,251,.98)",
            cardHoverBg: "#f0f2f5",
            groupBg: "rgba(31,41,55,.035)",
            hdrBg: "rgba(248,249,251,.96)",
            accent: "#6f62d0",
            accent2: "#5f54c4",
            accentRgb: "111,98,208",
            inputBg: "#f4f6f8",
            inputFocusBg: "#fff",
            btnBg: "#f3f5f7",
            btnHoverBg: "#e7eaf0",
            skeletonBase: "rgba(31,41,55,.07)",
            skeletonShine: "rgba(111,98,208,.15)",
            success: "#25835f",
            successRgb: "37,131,95",
            danger: "#ad4b5a",
            dangerRgb: "173,75,90",
            warning: "#a66f19",
            dockTileSurface: "#ffffff",
            dockAccentContrast: "#ffffff"
          }),
          dark: Object.freeze({
            backdropBg: "rgba(3,6,14,.72)",
            panelBg: "rgba(16,19,27,.98)",
            panelBorder: "rgba(255,255,255,.10)",
            panelShadow: "0 32px 90px rgba(0,0,0,.58)",
            panelGlow: "radial-gradient(circle at top,rgba(136,119,245,.14),transparent 40%)",
            textPrimary: "#f4f5f8",
            textSecondary: "#b3b8c4",
            textTertiary: "#777f8d",
            borderSoft: "rgba(255,255,255,.08)",
            borderMedium: "rgba(255,255,255,.14)",
            borderStrong: "rgba(255,255,255,.22)",
            cardBg: "rgba(26,30,39,.98)",
            cardHoverBg: "#222833",
            groupBg: "rgba(255,255,255,.035)",
            hdrBg: "rgba(18,22,30,.96)",
            accent: "#8877f5",
            accent2: "#a38cf5",
            accentRgb: "136,119,245",
            inputBg: "#1c212b",
            inputFocusBg: "#232a36",
            btnBg: "#1d222c",
            btnHoverBg: "#2b3240",
            skeletonBase: "rgba(255,255,255,.06)",
            skeletonShine: "rgba(136,119,245,.2)",
            success: "#73cba7",
            successRgb: "115,203,167",
            danger: "#e39aa5",
            dangerRgb: "227,154,165",
            warning: "#dfbd83",
            dockTileSurface: "#1c212b",
            dockAccentContrast: "#ffffff"
          })
        });
        function normalizeThemePreference(value) {
          const normalized = String(value || "").trim().toLowerCase();
          if (normalized === "system" || normalized === "light" || normalized === "dark") {
            return Object.freeze({ value: normalized, migrated: false });
          }
          if (LEGACY_LIGHT_THEMES.has(normalized)) return Object.freeze({ value: "light", migrated: true });
          if (LEGACY_DARK_THEMES.has(normalized)) return Object.freeze({ value: "dark", migrated: true });
          return Object.freeze({ value: "system", migrated: false });
        }
        function normalizeThemeId(value) {
          return normalizeThemePreference(value).value;
        }
        function resolveTheme(preference, prefersDark = false) {
          const normalized = normalizeThemePreference(preference).value;
          return normalized === "system" ? prefersDark ? "dark" : "light" : normalized;
        }
        function create(deps = {}) {
          const {
            chrome: chrome2,
            window: windowRef = {},
            document: document2,
            context,
            showToast = () => {
            },
            createMenu,
            closeAllMenus = () => {
            },
            mountAnchoredMenu,
            translate = () => ""
          } = deps;
          const colorScheme = windowRef.matchMedia?.("(prefers-color-scheme: dark)") || null;
          let preference = "system";
          let resolvedTheme = resolveTheme(preference, colorScheme?.matches === true);
          let started = false;
          let listening = false;
          let stateRevision = 0;
          let lifecycleEpoch = 0;
          const ownedStyles = /* @__PURE__ */ new Map();
          const tr = (key, fallback, params = {}) => {
            const translated = translate(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          const ctx = () => (typeof context === "function" ? context() : context) || {};
          function themeLabel(id = preference) {
            const theme = THEMES.find((item) => item.id === id) || THEMES[0];
            return tr(theme.labelKey, theme.fallback);
          }
          function apply() {
            const { host, root: shadowRoot } = ctx();
            resolvedTheme = resolveTheme(preference, colorScheme?.matches === true);
            if (!host || !shadowRoot) return resolvedTheme;
            host.setAttribute("data-theme", resolvedTheme);
            host.setAttribute("data-theme-preference", preference);
            const colors = THEME_COLORS[resolvedTheme];
            let style = shadowRoot.getElementById?.("tfa-theme-override");
            if (!style && document2?.createElement) {
              style = document2.createElement("style");
              style.id = "tfa-theme-override";
              shadowRoot.appendChild(style);
              ownedStyles.set(style, shadowRoot);
            }
            if (style) {
              style.textContent = `:host{--tfa-backdrop-bg:${colors.backdropBg}!important;--tfa-panel-bg:${colors.panelBg}!important;--tfa-panel-border:${colors.panelBorder}!important;--tfa-panel-shadow:${colors.panelShadow}!important;--tfa-panel-glow:${colors.panelGlow}!important;--tfa-text-primary:${colors.textPrimary}!important;--tfa-text-secondary:${colors.textSecondary}!important;--tfa-text-tertiary:${colors.textTertiary}!important;--tfa-border-soft:${colors.borderSoft}!important;--tfa-border-medium:${colors.borderMedium}!important;--tfa-border-strong:${colors.borderStrong}!important;--tfa-card-bg:${colors.cardBg}!important;--tfa-card-hover-bg:${colors.cardHoverBg}!important;--tfa-group-bg:${colors.groupBg}!important;--tfa-hdr-bg:${colors.hdrBg}!important;--tfa-accent:${colors.accent}!important;--tfa-accent-2:${colors.accent2}!important;--tfa-accent-rgb:${colors.accentRgb}!important;--tfa-input-bg:${colors.inputBg}!important;--tfa-input-focus-bg:${colors.inputFocusBg}!important;--tfa-btn-bg:${colors.btnBg}!important;--tfa-btn-hover-bg:${colors.btnHoverBg}!important;--tfa-skeleton-base:${colors.skeletonBase}!important;--tfa-skeleton-shine:${colors.skeletonShine}!important;--tfa-success:${colors.success}!important;--tfa-success-rgb:${colors.successRgb}!important;--tfa-danger:${colors.danger}!important;--tfa-danger-rgb:${colors.dangerRgb}!important;--tfa-warning:${colors.warning}!important;--dock-tile-surface:${colors.dockTileSurface}!important;--dock-accent-contrast:${colors.dockAccentContrast}!important}
.card,.panel,.settings-dialog,.dock-add-dialog,.dock-search-dialog,.color-picker-inner{color:var(--tfa-text-primary)!important}
.card{background:var(--tfa-card-bg)!important;border-color:var(--tfa-border-soft)!important}.card:hover{background:var(--tfa-card-hover-bg)!important;border-color:var(--tfa-border-medium)!important}
.card.active-tab,.card.selected{border-color:rgba(var(--tfa-accent-rgb),.52)!important;background:rgba(var(--tfa-accent-rgb),.12)!important}
.card-title{color:var(--tfa-text-primary)!important}.card-host,.card-close{color:var(--tfa-text-tertiary)!important}
.btn,.mini-btn{background:var(--tfa-btn-bg)!important;color:var(--tfa-text-secondary)!important;border-color:var(--tfa-border-soft)!important}.btn:hover,.mini-btn:hover{background:var(--tfa-btn-hover-bg)!important}
.btn.accent{background:linear-gradient(135deg,var(--tfa-accent),var(--tfa-accent-2))!important;color:#fff!important;border-color:transparent!important}
.search{background:var(--tfa-input-bg)!important;color:var(--tfa-text-primary)!important;border-color:var(--tfa-border-soft)!important}.search:focus{background:var(--tfa-input-focus-bg)!important;border-color:rgba(var(--tfa-accent-rgb),.54)!important}
.panel{background:var(--tfa-panel-bg)!important;border-color:var(--tfa-panel-border)!important;box-shadow:var(--tfa-panel-shadow)!important}.backdrop,.settings-dialog-overlay,.dock-add-overlay,.dock-search-overlay{background:var(--tfa-backdrop-bg)!important}.hdr,.ftr{background:var(--tfa-hdr-bg)!important}`;
            }
            const footer = shadowRoot.getElementById?.("ftrTheme");
            if (footer) footer.textContent = themeLabel();
            return resolvedTheme;
          }
          async function persistCanonical(value) {
            try {
              await chrome2?.storage?.sync?.set?.({ [THEME_STORAGE_KEY]: value });
            } catch (_error) {
            }
          }
          function setPreference(value, { migrate = false } = {}) {
            const normalized = normalizeThemePreference(value);
            preference = normalized.value;
            apply();
            if (migrate && normalized.migrated) void persistCanonical(preference);
            return normalized;
          }
          function onStorage(changes, area) {
            if (area !== "sync" || !changes?.[THEME_STORAGE_KEY]) return;
            stateRevision += 1;
            setPreference(changes[THEME_STORAGE_KEY].newValue, { migrate: true });
          }
          function onColorSchemeChange() {
            if (preference === "system") apply();
          }
          async function start() {
            if (started) return false;
            started = true;
            const epoch = ++lifecycleEpoch;
            const revision = stateRevision;
            chrome2?.storage?.onChanged?.addListener?.(onStorage);
            colorScheme?.addEventListener?.("change", onColorSchemeChange);
            if (!colorScheme?.addEventListener) colorScheme?.addListener?.(onColorSchemeChange);
            listening = true;
            let values = {};
            try {
              values = await chrome2?.storage?.sync?.get?.(THEME_STORAGE_KEY) || {};
            } catch (_error) {
            }
            if (!started || epoch !== lifecycleEpoch || revision !== stateRevision) return started;
            const normalized = setPreference(values[THEME_STORAGE_KEY]);
            if (normalized.migrated) await persistCanonical(normalized.value);
            return started && epoch === lifecycleEpoch;
          }
          function destroy() {
            if (!started && !listening) return false;
            started = false;
            lifecycleEpoch += 1;
            stateRevision += 1;
            if (listening) {
              chrome2?.storage?.onChanged?.removeListener?.(onStorage);
              colorScheme?.removeEventListener?.("change", onColorSchemeChange);
              if (!colorScheme?.removeEventListener) colorScheme?.removeListener?.(onColorSchemeChange);
            }
            for (const [style, shadowRoot] of ownedStyles) {
              if (shadowRoot?.getElementById?.("tfa-theme-override") === style) style.remove?.();
            }
            ownedStyles.clear();
            listening = false;
            return true;
          }
          async function selectTheme(value) {
            const normalized = normalizeThemePreference(value);
            if (!THEMES.some((item) => item.id === normalized.value) || normalized.migrated && value !== normalized.value) return false;
            stateRevision += 1;
            preference = normalized.value;
            apply();
            await persistCanonical(preference);
            return true;
          }
          function openMenu(anchor) {
            if (typeof createMenu !== "function" || typeof mountAnchoredMenu !== "function") return false;
            closeAllMenus();
            const menu = createMenu();
            menu.classList.add("theme-menu");
            menu.innerHTML = `<div class="menu-label">${tr("theme.title", "\u4E3B\u9898\u5916\u89C2")}</div>`;
            THEMES.forEach((theme) => {
              const item = document2.createElement("div");
              item.className = `menu-item theme-item${theme.id === preference ? " active" : ""}`;
              item.innerHTML = `<span class="mi-emoji">${theme.icon}</span><span class="mi-title">${themeLabel(theme.id)}</span>${theme.id === preference ? '<span class="theme-check">&#x2713;</span>' : ""}`;
              item.addEventListener("click", async () => {
                await selectTheme(theme.id);
                closeAllMenus();
                showToast(tr("theme.changed", "\u5DF2\u5207\u6362\u4E3A{theme}", { theme: themeLabel(theme.id) }), "info", 1200);
              });
              menu.appendChild(item);
            });
            mountAnchoredMenu(menu, anchor, { alignRight: true });
            return true;
          }
          return Object.freeze({
            start,
            destroy,
            apply,
            openMenu,
            selectTheme,
            openColorPicker: () => false,
            closeColorPicker: () => false,
            getCurrent: () => preference,
            getResolved: () => resolvedTheme
          });
        }
        return Object.freeze({
          THEME_STORAGE_KEY,
          THEMES,
          THEME_COLORS,
          normalizeThemePreference,
          normalizeThemeId,
          resolveTheme,
          create
        });
      });
    }
  });

  // src/content/activation-runtime.js
  var require_activation_runtime = __commonJS({
    "src/content/activation-runtime.js"(exports, module) {
      (function attachActivationRuntime(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaActivationRuntime = api;
        else if (!commonJs && root) root.TfaActivationRuntime = api;
      })(
        typeof globalThis === "object" ? globalThis : exports,
        function createActivationRuntimeModule() {
          "use strict";
          function create(deps) {
            const {
              chrome: chrome2,
              window: window2,
              document: document2,
              send,
              showToast = () => {
              },
              translate = () => "",
              onStateChange = () => {
              },
              onLayerChange = () => {
              },
              context,
              ui = {},
              delay: injectedDelay,
              minVerifyMs = 1300
            } = deps;
            const escapeHtml = ui.escapeHtml || ((value) => String(value || ""));
            const tr = (key, fallback, params = {}) => {
              const translated = translate(key, params);
              return String(translated && translated !== key ? translated : fallback).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
            };
            const maskCode = ui.maskCodeForDisplay || ((value) => String(value || ""));
            let state = { status: "inactive" };
            let requestPending = false;
            let deactivatePending = false;
            let loadError = "";
            let draft = "";
            let purchasePreview = null;
            let deactivationDialog = null;
            let purchaseExpanded = true;
            let activeCollapseInitialized = false;
            let started = false;
            let lifecycleEpoch = 0;
            let verificationTimer = null;
            let verificationResolve = null;
            const waitForVerificationFloor = (ms) => {
              if (injectedDelay) return injectedDelay(ms);
              return new Promise((resolve) => {
                verificationResolve = resolve;
                verificationTimer = setTimeout(() => {
                  verificationTimer = null;
                  verificationResolve = null;
                  resolve();
                }, ms);
              });
            };
            function cancelVerificationFloor() {
              if (verificationTimer !== null) clearTimeout(verificationTimer);
              verificationTimer = null;
              const resolve = verificationResolve;
              verificationResolve = null;
              if (resolve) resolve();
            }
            const ctx = () => (typeof context === "function" ? context() : context) || {};
            const settings = () => ctx().settingsDialog || null;
            const rootNode = () => ctx().root || null;
            function focusActivationInput() {
              const dialog = settings();
              const input = dialog && dialog.querySelector("#activationCodeInput");
              if (input) input.focus();
            }
            const online = () => !window2.navigator || window2.navigator.onLine !== false;
            const formatTime = (value) => {
              try {
                const date = new Date(value || "");
                if (Number.isNaN(date.getTime())) return tr("activation.localRecord", "\u672C\u673A\u8BB0\u5F55");
                return date.toLocaleString(tr("activation.locale", "zh-CN"), {
                  year: "numeric",
                  month: "2-digit",
                  day: "2-digit",
                  hour: "2-digit",
                  minute: "2-digit",
                  hour12: false
                });
              } catch (_) {
                return tr("activation.localRecord", "\u672C\u673A\u8BB0\u5F55");
              }
            };
            function setState(next) {
              state = next && typeof next === "object" ? next : { status: "inactive" };
              if (state.status === "active") {
                if (!activeCollapseInitialized) purchaseExpanded = false;
                activeCollapseInitialized = true;
              } else {
                purchaseExpanded = true;
                activeCollapseInitialized = false;
              }
              onStateChange(state);
              render();
            }
            async function refresh() {
              const operationEpoch = lifecycleEpoch;
              let response;
              try {
                response = await send({ type: "LICENSE_GET_STATE" });
              } catch (error) {
                response = { ok: false, message: error && error.message };
              }
              if (!started || operationEpoch !== lifecycleEpoch) return null;
              if (response && response.ok && response.state) {
                loadError = "";
                setState(response.state);
                return state;
              }
              loadError = response && response.message ? response.message : tr("activation.loadFailed", "\u8BFB\u53D6\u6FC0\u6D3B\u72B6\u6001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
              render();
              return state;
            }
            function purchaseMarkup() {
              const imageUrl = chrome2.runtime.getURL("activation-purchase.png");
              if (!purchaseExpanded)
                return `<div class="activation-purchase-row"><div><div class="activation-card-title">${escapeHtml(tr("activation.purchaseGuide", "\u95F2\u9C7C\u8D2D\u4E70\u5F15\u5BFC\u56FE"))}</div><div class="activation-helper">${escapeHtml(state.status === "active" ? tr("activation.purchaseActiveHint", "\u6FC0\u6D3B\u540E\u9ED8\u8BA4\u6298\u53E0\uFF0C\u4ECD\u53EF\u91CD\u65B0\u5C55\u5F00") : tr("activation.purchaseHint", "\u626B\u7801\u8D2D\u4E70\u6C38\u4E45\u6FC0\u6D3B\u7801"))}</div></div><button class="activation-link-btn" id="activationPurchaseToggle" type="button" aria-expanded="false">${escapeHtml(tr("activation.expandImage", "\u5C55\u5F00\u56FE\u7247"))}</button></div>`;
              return `<section class="activation-card activation-purchase-card" aria-label="${escapeHtml(tr("activation.purchaseAria", "\u8D2D\u4E70\u6FC0\u6D3B\u7801"))}"><div class="activation-purchase-layout"><button class="activation-image-button" id="activationPurchaseZoom" type="button" aria-label="${escapeHtml(tr("activation.zoomAria", "\u653E\u5927\u67E5\u770B\u95F2\u9C7C\u8D2D\u4E70\u5F15\u5BFC\u56FE"))}"><img class="activation-purchase-image" src="${imageUrl}" alt="${escapeHtml(tr("activation.imageAlt", "\u95F2\u9C7C\u5E97\u94FA\u8D2D\u4E70\u5F15\u5BFC\u53CA\u4E8C\u7EF4\u7801"))}"></button><div class="activation-purchase-copy"><div class="activation-eyebrow">${escapeHtml(tr("activation.purchaseEyebrow", "\u8D2D\u4E70\u6307\u5F15"))}</div><div class="activation-card-title">${escapeHtml(tr("activation.noCode", "\u8FD8\u6CA1\u6709\u6FC0\u6D3B\u7801\uFF1F"))}</div><p class="activation-copy">${escapeHtml(tr("activation.purchaseCopy", "\u4FDD\u5B58\u6216\u653E\u5927\u56FE\u7247\u540E\uFF0C\u4F7F\u7528\u95F2\u9C7C\u626B\u4E00\u626B\u8FDB\u5165\u5E97\u94FA\u8D2D\u4E70\u3002\u8D2D\u4E70\u5B8C\u6210\u540E\u4F1A\u6536\u5230\u6C38\u4E45\u6FC0\u6D3B\u7801\u3002"))}</p><div class="activation-inline-actions"><button class="activation-link-btn" id="activationPurchaseZoomText" type="button">${escapeHtml(tr("activation.zoom", "\u653E\u5927\u67E5\u770B"))}</button><button class="activation-link-btn" id="activationPurchaseToggle" type="button" aria-expanded="true">${escapeHtml(tr("activation.collapseImage", "\u6536\u8D77\u56FE\u7247"))}</button></div></div></div></section>`;
            }
            function render() {
              const dialog = settings();
              if (!dialog) return;
              const panel = dialog.querySelector("#activationPanel");
              if (!panel) return;
              if (state.status === "active") {
                const record = state.record || {};
                panel.innerHTML = `<section class="activation-success" aria-live="polite"><div class="activation-success-icon" aria-hidden="true">\u2713</div><div><div class="activation-eyebrow">${escapeHtml(tr("activation.memberStatus", "\u4F1A\u5458\u72B6\u6001"))}</div><h3>${escapeHtml(tr("activation.active", "\u5DF2\u6C38\u4E45\u6FC0\u6D3B"))}</h3><p class="activation-copy">${escapeHtml(tr("activation.unlocked", "\u589E\u503C\u529F\u80FD\u5DF2\u89E3\u9501"))}</p><div class="activation-used-note">${escapeHtml(tr("activation.used", "\u8BE5\u6FC0\u6D3B\u7801\u5DF2\u4F7F\u7528\u5E76\u4F5C\u5E9F"))}</div><div class="activation-code-mask">${escapeHtml(maskCode(record.code))}</div><div class="activation-helper">${escapeHtml(tr("activation.activatedAt", "\u6FC0\u6D3B\u65F6\u95F4\uFF1A{time}", { time: formatTime(record.activatedAt) }))}</div><div class="activation-success-actions"><button class="activation-deactivate" id="activationDeactivate" type="button">${escapeHtml(tr("activation.deactivate", "\u6CE8\u9500\u6FC0\u6D3B"))}</button></div></div></section>${purchaseMarkup()}`;
                bind();
                return;
              }
              const error = loadError || state.status === "invalid" && (state.message || tr("activation.invalid", "\u6FC0\u6D3B\u7801\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u65B0\u8F93\u5165")) || "";
              panel.innerHTML = `${purchaseMarkup()}<section class="activation-card"><div class="activation-eyebrow">${escapeHtml(tr("activation.eyebrow", "\u6C38\u4E45\u6FC0\u6D3B"))}</div><div class="activation-card-title">${escapeHtml(tr("activation.inputTitle", "\u8F93\u5165\u8D2D\u4E70\u540E\u6536\u5230\u7684\u6FC0\u6D3B\u7801"))}</div><p class="activation-copy">${escapeHtml(tr("activation.copy", "\u65E0\u9700\u767B\u5F55\uFF0C\u8054\u7F51\u540E\u5373\u53EF\u9A8C\u8BC1\u3002\u672C\u673A\u5C06\u6C38\u4E45\u4FDD\u5B58\u6FC0\u6D3B\u72B6\u6001\uFF0C\u91CD\u88C5\u540E\u53EF\u518D\u6B21\u8F93\u5165\u539F\u6FC0\u6D3B\u7801\u6062\u590D\u3002"))}</p><form id="activationForm" novalidate aria-busy="${requestPending}"><div class="activation-form-row"><label class="sr-only" for="activationCodeInput">${escapeHtml(tr("activation.code", "\u6FC0\u6D3B\u7801"))}</label><input class="activation-input" id="activationCodeInput" type="text" inputmode="text" autocomplete="off" spellcheck="false" placeholder="AK-XXXX-XXXX-XXXX-XXXX-XXXX" ${requestPending ? "disabled" : ""}><button class="btn accent activation-submit" id="activationSubmit" type="submit" ${requestPending ? "disabled" : ""}>${requestPending ? `<span class="activation-spinner" aria-hidden="true"></span><span>${escapeHtml(tr("activation.verifying", "\u6B63\u5728\u9A8C\u8BC1\u2026"))}</span>` : escapeHtml(tr("activation.submit", "\u9A8C\u8BC1\u5E76\u6FC0\u6D3B"))}</button></div><div class="activation-progress sr-only" aria-live="polite">${requestPending ? escapeHtml(tr("activation.progress", "\u6B63\u5728\u8FDE\u63A5\u5E76\u9A8C\u8BC1\u6FC0\u6D3B\u7801\uFF0C\u8BF7\u7A0D\u5019")) : ""}</div><div class="activation-error" id="activationError" role="alert" ${error ? "" : "hidden"}>${escapeHtml(error)}</div></form></section>`;
              const input = panel.querySelector("#activationCodeInput");
              if (input) input.value = draft;
              bind();
            }
            function refreshLanguage() {
              render();
              if (purchasePreview) {
                purchasePreview.setAttribute("aria-label", tr("activation.previewAria", "\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247\u9884\u89C8"));
                purchasePreview.querySelector("#purchasePreviewClose")?.setAttribute("aria-label", tr("activation.previewClose", "\u5173\u95ED\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247"));
                purchasePreview.querySelector("img")?.setAttribute("alt", tr("activation.imageAlt", "\u95F2\u9C7C\u5E97\u94FA\u8D2D\u4E70\u5F15\u5BFC\u53CA\u4E8C\u7EF4\u7801"));
              }
              if (deactivationDialog) {
                deactivationDialog.querySelector("#deactivationClose")?.setAttribute("aria-label", tr("activation.deactivateClose", "\u5173\u95ED\u6CE8\u9500\u786E\u8BA4"));
                const title = deactivationDialog.querySelector("#deactivationTitle"), copy = deactivationDialog.querySelector("#deactivationDialog > p"), cancel = deactivationDialog.querySelector("#deactivationCancel");
                if (title) title.textContent = tr("activation.deactivateTitle", "\u786E\u8BA4\u6CE8\u9500\u6FC0\u6D3B\uFF1F");
                if (copy) copy.textContent = tr("activation.deactivateCopy", "\u6CE8\u9500\u540E\u4F1A\u5458\u6807\u8BC6\u53CA\u5168\u90E8\u589E\u503C\u6743\u76CA\u5C06\u7ACB\u5373\u5931\u6548\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u4F7F\u7528\u540C\u4E00\u6FC0\u6D3B\u7801\u91CD\u65B0\u6FC0\u6D3B\u3002");
                if (cancel) cancel.textContent = tr("activation.cancel", "\u53D6\u6D88");
                setDeactivatePending(deactivatePending);
              }
              return true;
            }
            function bind() {
              const dialog = settings();
              if (!dialog) return;
              const form = dialog.querySelector("#activationForm");
              if (form)
                form.addEventListener("submit", (event) => {
                  event.preventDefault();
                  const input2 = dialog.querySelector("#activationCodeInput");
                  activate(input2 ? input2.value : draft);
                });
              const input = dialog.querySelector("#activationCodeInput");
              if (input)
                input.addEventListener("input", () => {
                  draft = input.value;
                  if (loadError) {
                    loadError = "";
                    const error = dialog.querySelector("#activationError");
                    if (error) error.hidden = true;
                  }
                });
              const deactivate2 = dialog.querySelector("#activationDeactivate");
              if (deactivate2)
                deactivate2.addEventListener("click", openDeactivationDialog);
              const toggle = dialog.querySelector("#activationPurchaseToggle");
              if (toggle)
                toggle.addEventListener("click", () => {
                  purchaseExpanded = !purchaseExpanded;
                  render();
                });
              dialog.querySelectorAll(
                "#activationPurchaseZoom, #activationPurchaseZoomText"
              ).forEach(
                (button) => button.addEventListener("click", openPurchasePreview)
              );
            }
            async function activate(code) {
              if (requestPending) return { ok: false, pending: true };
              draft = String(code || "").trim();
              if (!online()) {
                loadError = tr("activation.offline", "\u7F51\u7EDC\u4E0D\u53EF\u7528\uFF0C\u8BF7\u8FDE\u63A5\u7F51\u7EDC\u540E\u91CD\u8BD5");
                render();
                focusActivationInput();
                return { ok: false, offline: true };
              }
              const operationEpoch = lifecycleEpoch;
              requestPending = true;
              loadError = "";
              render();
              let response;
              try {
                [response] = await Promise.all([
                  send({ type: "LICENSE_ACTIVATE", code: draft }),
                  waitForVerificationFloor(minVerifyMs)
                ]);
              } catch (error) {
                response = { ok: false, message: error && error.message };
              }
              if (!started || operationEpoch !== lifecycleEpoch)
                return { ok: false, cancelled: true };
              requestPending = false;
              if (response && response.ok && response.state) {
                const activated = response.state.status === "active";
                if (activated) draft = "";
                setState(response.state);
                if (activated) showToast(tr("activation.active", "\u5DF2\u6C38\u4E45\u6FC0\u6D3B"), "success", 2200);
                else focusActivationInput();
                return response;
              }
              const failure = response ? { ...response, ok: false } : { ok: false };
              loadError = response && response.message ? response.message : tr("activation.failed", "\u6FC0\u6D3B\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
              render();
              focusActivationInput();
              return failure;
            }
            function openPurchasePreview() {
              const root = rootNode();
              if (!root || purchasePreview) return;
              const node = document2.createElement("div");
              node.className = "purchase-preview";
              node.setAttribute("role", "dialog");
              node.setAttribute("aria-modal", "true");
              node.setAttribute("aria-label", tr("activation.previewAria", "\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247\u9884\u89C8"));
              node.innerHTML = `<div class="purchase-preview-backdrop" data-preview-dismiss></div><div class="purchase-preview-dialog"><button class="purchase-preview-close" id="purchasePreviewClose" type="button" aria-label="${escapeHtml(tr("activation.previewClose", "\u5173\u95ED\u8D2D\u4E70\u5F15\u5BFC\u56FE\u7247"))}">\xD7</button><img class="purchase-preview-image" src="${chrome2.runtime.getURL("activation-purchase.png")}" alt="${escapeHtml(tr("activation.imageAlt", "\u95F2\u9C7C\u5E97\u94FA\u8D2D\u4E70\u5F15\u5BFC\u53CA\u4E8C\u7EF4\u7801"))}"></div>`;
              root.appendChild(node);
              purchasePreview = node;
              onLayerChange();
              node.classList.add("show");
              const close = node.querySelector("#purchasePreviewClose");
              close.addEventListener("click", () => closePurchasePreview());
              node.querySelector("[data-preview-dismiss]").addEventListener("click", () => closePurchasePreview());
              close.focus();
            }
            function closePurchasePreview(restoreFocus = true) {
              const node = purchasePreview;
              purchasePreview = null;
              if (node) {
                node.remove();
                onLayerChange();
              }
              if (restoreFocus) {
                const dialog = settings();
                const target = dialog && dialog.querySelector(
                  "#activationPurchaseZoom, #activationPurchaseZoomText"
                );
                if (target) target.focus();
              }
            }
            function openDeactivationDialog() {
              const root = rootNode();
              if (!root || deactivationDialog || state.status !== "active") return;
              const node = document2.createElement("div");
              node.className = "deactivation-confirm";
              node.innerHTML = `<div class="deactivation-backdrop" data-deactivation-dismiss></div><section class="deactivation-dialog" id="deactivationDialog" role="dialog" aria-modal="true" aria-labelledby="deactivationTitle"><button class="deactivation-close" id="deactivationClose" type="button" aria-label="${escapeHtml(tr("activation.deactivateClose", "\u5173\u95ED\u6CE8\u9500\u786E\u8BA4"))}">\xD7</button><div class="deactivation-icon" aria-hidden="true">!</div><h3 id="deactivationTitle">${escapeHtml(tr("activation.deactivateTitle", "\u786E\u8BA4\u6CE8\u9500\u6FC0\u6D3B\uFF1F"))}</h3><p>${escapeHtml(tr("activation.deactivateCopy", "\u6CE8\u9500\u540E\u4F1A\u5458\u6807\u8BC6\u53CA\u5168\u90E8\u589E\u503C\u6743\u76CA\u5C06\u7ACB\u5373\u5931\u6548\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u4F7F\u7528\u540C\u4E00\u6FC0\u6D3B\u7801\u91CD\u65B0\u6FC0\u6D3B\u3002"))}</p><div class="deactivation-error" id="deactivationError" role="alert" hidden></div><div class="deactivation-actions"><button class="btn" id="deactivationCancel" type="button">${escapeHtml(tr("activation.cancel", "\u53D6\u6D88"))}</button><button class="btn deactivation-danger" id="deactivationConfirm" type="button">${escapeHtml(tr("activation.confirmDeactivate", "\u786E\u8BA4\u6CE8\u9500"))}</button></div></section>`;
              root.appendChild(node);
              deactivationDialog = node;
              onLayerChange();
              node.querySelector("#deactivationCancel").addEventListener("click", () => closeDeactivationDialog());
              node.querySelector("#deactivationClose").addEventListener("click", () => closeDeactivationDialog());
              node.querySelector("[data-deactivation-dismiss]").addEventListener("click", () => closeDeactivationDialog());
              node.querySelector("#deactivationConfirm").addEventListener("click", deactivate);
              node.classList.add("show");
              node.querySelector("#deactivationCancel").focus();
            }
            function closeDeactivationDialog(restoreFocus = true) {
              if (!deactivationDialog || deactivatePending) return false;
              const node = deactivationDialog;
              deactivationDialog = null;
              node.remove();
              onLayerChange();
              if (restoreFocus) {
                const dialog = settings();
                const target = dialog && dialog.querySelector("#activationDeactivate");
                if (target) target.focus();
              }
              return true;
            }
            function forceCloseLayers() {
              lifecycleEpoch += 1;
              cancelVerificationFloor();
              requestPending = false;
              deactivatePending = false;
              closePurchasePreview(false);
              if (deactivationDialog) {
                const node = deactivationDialog;
                deactivationDialog = null;
                node.remove();
                onLayerChange();
              }
            }
            function setDeactivatePending(value) {
              deactivatePending = value;
              if (!deactivationDialog) return;
              [
                "#deactivationConfirm",
                "#deactivationCancel",
                "#deactivationClose"
              ].forEach((selector) => {
                const element = deactivationDialog.querySelector(selector);
                if (element) element.disabled = value;
              });
              const confirm = deactivationDialog.querySelector(
                "#deactivationConfirm"
              );
              if (confirm)
                confirm.innerHTML = value ? `<span class="activation-spinner" aria-hidden="true"></span><span>${escapeHtml(tr("activation.deactivating", "\u6B63\u5728\u6CE8\u9500\u2026"))}</span>` : tr("activation.confirmDeactivate", "\u786E\u8BA4\u6CE8\u9500");
              const dialog = deactivationDialog.querySelector("#deactivationDialog");
              if (dialog) dialog.setAttribute("aria-busy", String(value));
            }
            function showDeactivateError(message) {
              if (!deactivationDialog) return;
              const error = deactivationDialog.querySelector("#deactivationError");
              if (error) {
                error.hidden = false;
                error.textContent = message;
              }
            }
            async function deactivate() {
              if (deactivatePending || !deactivationDialog)
                return { ok: false, pending: true };
              if (!online()) {
                showDeactivateError(tr("activation.offline", "\u7F51\u7EDC\u4E0D\u53EF\u7528\uFF0C\u8BF7\u8FDE\u63A5\u7F51\u7EDC\u540E\u91CD\u8BD5"));
                return { ok: false, offline: true };
              }
              setDeactivatePending(true);
              const operationEpoch = lifecycleEpoch;
              let response;
              try {
                response = await send({ type: "LICENSE_DEACTIVATE" });
              } catch (error) {
                response = { ok: false, message: error && error.message };
              }
              if (!started || operationEpoch !== lifecycleEpoch)
                return { ok: false, cancelled: true };
              if (response && response.ok && response.state && response.state.status === "inactive") {
                const node = deactivationDialog;
                deactivationDialog = null;
                deactivatePending = false;
                if (node) {
                  node.remove();
                  onLayerChange();
                }
                draft = "";
                loadError = "";
                setState(response.state);
                showToast(tr("activation.deactivated", "\u5DF2\u6CE8\u9500\u6FC0\u6D3B"), "success", 2200);
                return response;
              }
              setDeactivatePending(false);
              showDeactivateError(
                response && response.message ? response.message : tr("activation.deactivateFailed", "\u6CE8\u9500\u6FC0\u6D3B\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
              );
              return response || { ok: false };
            }
            function onStorage(changes, area) {
              if (area === "local" && changes["license.record.v1"] && !requestPending && !deactivatePending)
                refresh();
            }
            function start() {
              if (started) return;
              started = true;
              lifecycleEpoch += 1;
              chrome2.storage.onChanged.addListener(onStorage);
            }
            function destroy() {
              if (!started) return;
              started = false;
              chrome2.storage.onChanged.removeListener(onStorage);
              forceCloseLayers();
            }
            return Object.freeze({
              start,
              destroy,
              refresh,
              render,
              refreshLanguage,
              activate,
              deactivate,
              setState,
              openPurchasePreview,
              closePurchasePreview,
              openDeactivationDialog,
              closeDeactivationDialog,
              forceCloseLayers,
              hasPurchasePreview: () => !!purchasePreview,
              hasDeactivationDialog: () => !!deactivationDialog,
              isPending: () => requestPending || deactivatePending,
              isRequestPending: () => requestPending,
              isDeactivationPending: () => deactivatePending,
              getDraft: () => draft,
              getState: () => state
            });
          }
          return Object.freeze({ create });
        }
      );
    }
  });

  // src/content/tab-actions.js
  var require_tab_actions = __commonJS({
    "src/content/tab-actions.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaTabActions = api;
        else if (!commonJs) root.TfaTabActions = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/;
        function suggestionId(value) {
          const normalized = String(value == null ? "" : value).trim();
          if (!normalized.startsWith("suggestion-") || !SAFE_ID.test(normalized)) {
            throw new TypeError("Invalid suggestionId");
          }
          return normalized;
        }
        function changeId(value) {
          const normalized = String(value == null ? "" : value).trim();
          if (!normalized.startsWith("change-") || !SAFE_ID.test(normalized)) throw new TypeError("Invalid changeId");
          return normalized;
        }
        function pageIds(value) {
          if (!Array.isArray(value)) throw new TypeError("pageIds must be an array");
          const seen = /* @__PURE__ */ new Set();
          return value.reduce((result, item) => {
            if (typeof item !== "string") throw new TypeError("pageIds must contain only page ids");
            const normalized = item.trim();
            if (!normalized.startsWith("page-") || !SAFE_ID.test(normalized)) {
              throw new TypeError("Invalid pageId");
            }
            if (!seen.has(normalized)) {
              seen.add(normalized);
              result.push(normalized);
            }
            return result;
          }, []);
        }
        function suggestionDecision(value) {
          if (value !== "accept" && value !== "reject") throw new TypeError("Invalid suggestion decision");
          return value;
        }
        function createTabActions(deps = {}) {
          const send = typeof deps.send === "function" ? deps.send : async () => ({ error: "unavailable" });
          let destroyed = false;
          const request = (message) => destroyed ? Promise.resolve({ error: "destroyed" }) : send(message);
          const api = {
            getAllTabs: () => request({ type: "GET_ALL_TABS" }),
            getAllScreenshots: () => request({ type: "GET_ALL_SCREENSHOTS" }),
            getHotBookmarks: () => request({ type: "GET_HOT_BOOKMARKS" }),
            getRecentlyClosed: () => request({ type: "GET_RECENTLY_CLOSED" }),
            consumeRecentlyClosedItem: (itemId, url) => request({ type: "CONSUME_RECENTLY_CLOSED_ITEM", itemId, url }),
            getTrash: (options = {}) => request(options.includeTasks === true ? { type: "GET_TRASH", includeTasks: true } : { type: "GET_TRASH" }),
            addTaskToTrash: (task, operationToken = "", options = {}) => request(options?.prepared === true ? { type: "ADD_TASK_TO_TRASH", task, operationToken, prepared: true } : operationToken ? { type: "ADD_TASK_TO_TRASH", task, operationKey: operationToken } : { type: "ADD_TASK_TO_TRASH", task }),
            updateTaskTrashOperation: (operationToken, phase, id) => request({
              type: "UPDATE_TASK_TRASH_OPERATION",
              operationToken,
              phase,
              ...id ? { id } : {}
            }),
            getTaskTrashOperations: () => request({ type: "GET_TASK_TRASH_OPERATIONS" }),
            clearTrash: () => request({ type: "CLEAR_TRASH" }),
            restoreTrashItem: (id, options = {}) => request(options.restoreBatchId ? { type: "RESTORE_TRASH_ITEM", id, restoreBatchId: String(options.restoreBatchId) } : { type: "RESTORE_TRASH_ITEM", id }),
            deleteTrashItem: (id, options = {}) => request(options.itemType ? { type: "DELETE_TRASH_ITEM", id, itemType: options.itemType } : { type: "DELETE_TRASH_ITEM", id }),
            closeGroup: (groupId) => request({ type: "CLOSE_GROUP", groupId }),
            addToGroup: (tabIds, groupId) => request({ type: "ADD_TO_GROUP", tabIds, groupId }),
            updateGroup: (groupId, title, color) => request({ type: "UPDATE_GROUP", groupId, title, color }),
            ungroupTabs: (tabIds) => request({ type: "UNGROUP_TABS", tabIds }),
            groupTabs: (tabIds, title, color) => request({ type: "GROUP_TABS", tabIds, title, color }),
            groupByDomain: (windowId) => request(Number.isInteger(Number(windowId)) ? { type: "GROUP_BY_DOMAIN", windowId: Number(windowId) } : { type: "GROUP_BY_DOMAIN" }),
            ungroupAll: (windowId) => request(Number.isInteger(Number(windowId)) ? { type: "UNGROUP_ALL", windowId: Number(windowId) } : { type: "UNGROUP_ALL" }),
            closeTabs: (tabIds) => request({ type: "CLOSE_TABS", tabIds }),
            pinTab: (tabId, pinned) => request({ type: "PIN_TAB", tabId, pinned }),
            duplicateTab: (tabId) => request({ type: "DUPLICATE_TAB", tabId }),
            switchTab: (tabId) => request({ type: "SWITCH_TAB", tabId }),
            moveTab: (tabId, index, windowId) => request({ type: "MOVE_TAB", tabId, index, windowId }),
            moveTabRelative: (tabId, target = {}) => request({
              type: "MOVE_TAB_RELATIVE",
              tabId,
              beforeTabId: target.beforeTabId ?? null,
              afterTabId: target.afterTabId ?? null,
              placement: target.placement || "relative",
              windowId: target.windowId,
              groupId: target.groupId
            }),
            toggleDockSite: (site) => request({ type: "DOCK_TOGGLE_SITE", site }),
            pinAsBookmark: (tabId) => request({ type: "PIN_AS_BOOKMARK", tabId }),
            newTab: (url, active = true) => request({ type: "NEW_TAB", url, active }),
            restoreSession: (sessionId) => request({ type: "RESTORE_SESSION", sessionId }),
            listTaskData: () => request({ type: "V2_TASK_LIST" }),
            listInboxPages: () => request({ type: "V2_INBOX_LIST" }),
            setInboxPreference: (action, pageId, url) => request({ type: "V2_INBOX_PREFERENCE_SET", action, pageId, url }),
            createTask: (task) => request({ type: "V2_TASK_CREATE", task }),
            updateTask: (taskId, patch) => request({ type: "V2_TASK_UPDATE", taskId, patch }),
            reorderTasks: (orderedTaskIds) => request({ type: "V2_TASK_REORDER", orderedTaskIds }),
            deleteTask: (taskId, replacementTaskIdsByPage = {}, operationToken = "") => request({
              type: "V2_TASK_DELETE",
              taskId,
              replacementTaskIdsByPage,
              ...operationToken ? { operationToken } : {}
            }),
            addTaskRelation: (taskId, page, options = {}) => request({ type: "V2_RELATION_ADD", taskId, page, ...options }),
            setPrimaryTaskRelation: (taskId, pageId) => request({ type: "V2_RELATION_ADD", taskId, pageId, isPrimary: true }),
            removeTaskRelation: (taskId, pageId, replacementTaskId = "") => request({ type: "V2_RELATION_REMOVE", taskId, pageId, ...replacementTaskId ? { replacementTaskId } : {} }),
            moveTaskRelation: (sourceTaskId, targetTaskId, pageId) => request({ type: "V2_RELATION_MOVE", sourceTaskId, targetTaskId, pageId }),
            reorderTaskRelations: (taskId, orderedPageIds) => request({ type: "V2_RELATION_REORDER", taskId, orderedPageIds }),
            restoreTaskData: (task, restoreToken = "") => request({ type: "V2_TASK_RESTORE", task, restoreToken }),
            undoLastTaskChange: () => request({ type: "V2_TASK_UNDO_LAST" }),
            clearTaskData: () => request({ type: "V2_TASK_DATA_CLEAR" }),
            refreshSuggestions: (ids) => request({ type: "V2_SUGGESTION_REFRESH", pageIds: pageIds(ids) }),
            listSuggestions: (ids) => request({ type: "V2_SUGGESTION_LIST", pageIds: pageIds(ids) }),
            organizeSuggestions: (ids, locale = "en") => request({
              type: "V2_SUGGESTION_ORGANIZE",
              pageIds: pageIds(ids),
              locale: locale === "zh-CN" ? "zh-CN" : "en"
            }),
            resolveSuggestion: (id, decision) => request({
              type: "V2_SUGGESTION_RESOLVE",
              suggestionId: suggestionId(id),
              decision: suggestionDecision(decision)
            }),
            undoSuggestion: (id) => request({ type: "V2_SUGGESTION_UNDO", suggestionId: suggestionId(id) }),
            undoAiPlan: (id) => request({ type: "V2_AI_PLAN_UNDO", changeId: changeId(id) })
          };
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            return true;
          }
          function inspect() {
            return { destroyed };
          }
          return Object.freeze({ ...api, destroy, inspect });
        }
        return { createTabActions };
      });
    }
  });

  // src/content/dock-favicon-runtime.js
  var require_dock_favicon_runtime = __commonJS({
    "src/content/dock-favicon-runtime.js"(exports, module) {
      (function attachDockFaviconRuntime(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockFaviconRuntime = api;
        else if (!commonJs && root) root.TfaDockFaviconRuntime = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createDockFaviconRuntimeModule() {
        "use strict";
        function createDockFaviconRuntime(deps) {
          const { chrome: chrome2, document: document2, send, context, WebDockCatalog } = deps, { normalizeDockUrl, safeEphemeralDockIconUrl, normalizeDockIconText, serializedByteLength } = deps;
          const { compareDockIconCandidates, requiredDockFaviconPixels, selectDockFaviconSize } = deps;
          const { isFinalDockFaviconCandidate, isCurrentWebDockLifecycle, getWebDockItems } = deps;
          const { syncDockSettingsFaviconState, onCustomIconsChanged } = deps;
          const setTimer = deps.setTimer || setTimeout, clearTimer = deps.clearTimer || clearTimeout;
          const getDevicePixelRatio = deps.getDevicePixelRatio || (() => 1);
          const DOCK_FAVICON_RETRY_DELAYS = deps.retryDelays || Object.freeze([0, 350, 1200]);
          const DOCK_FAVICON_LOAD_TIMEOUT_MS = deps.loadTimeoutMs || 2500;
          const DOCK_FAVICON_DECODE_TIMEOUT_MS = deps.decodeTimeoutMs || 7500;
          const WEB_DOCK_CUSTOM_ICONS_KEY = deps.customIconsKey || "tfa_web_dock_custom_icons_v1";
          const WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH = deps.customIconMaxDataUrlLength || 60 * 1024;
          const WEB_DOCK_CUSTOM_ICON_MAP_MAX_BYTES = deps.customIconMapMaxBytes || 4 * 1024 * 1024;
          let webDockCustomIcons = {}, webDockCustomIconsLoaded = false, webDockCustomIconsLoadPromise = null;
          let webDockFaviconStates = /* @__PURE__ */ new Map(), webDockFaviconRequestSequence = 0, destroyed = false;
          const current = () => (typeof context === "function" ? context() : context) || {};
          function chromeDockFaviconUrl(site) {
            const faviconUrl = new URL(chrome2.runtime.getURL("/_favicon/"));
            faviconUrl.searchParams.set("pageUrl", site.url);
            faviconUrl.searchParams.set("size", String(selectDockFaviconSize(current().config.motion, getDevicePixelRatio())));
            return faviconUrl.href;
          }
          function normalizeWebDockCustomIconMap(value) {
            const normalized = {};
            if (!value || typeof value !== "object" || Array.isArray(value)) return normalized;
            for (const [rawUrl, rawIcon] of Object.entries(value)) {
              let url;
              try {
                url = normalizeDockUrl(rawUrl);
              } catch (_error) {
                continue;
              }
              const icon = safeEphemeralDockIconUrl(rawIcon, url);
              if (!icon || !icon.startsWith("data:image/") || icon.length > WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH) continue;
              normalized[url] = icon;
            }
            return normalized;
          }
          async function loadWebDockCustomIcons() {
            if (webDockCustomIconsLoaded) return webDockCustomIcons;
            if (webDockCustomIconsLoadPromise) return webDockCustomIconsLoadPromise;
            webDockCustomIconsLoadPromise = (async () => {
              try {
                const stored = await chrome2.storage.local.get(WEB_DOCK_CUSTOM_ICONS_KEY);
                webDockCustomIcons = normalizeWebDockCustomIconMap(stored && stored[WEB_DOCK_CUSTOM_ICONS_KEY]);
              } catch (_error) {
                webDockCustomIcons = {};
              }
              webDockCustomIconsLoaded = true;
              webDockCustomIconsLoadPromise = null;
              return webDockCustomIcons;
            })();
            return webDockCustomIconsLoadPromise;
          }
          function refreshWebDockCustomIcons(nextMap) {
            const normalized = normalizeWebDockCustomIconMap(nextMap);
            if (JSON.stringify(normalized) === JSON.stringify(webDockCustomIcons)) return false;
            webDockCustomIcons = normalized;
            webDockCustomIconsLoaded = true;
            clearWebDockFaviconTimers();
            webDockFaviconRequestSequence += 1;
            webDockFaviconStates = /* @__PURE__ */ new Map();
            if (typeof onCustomIconsChanged === "function") onCustomIconsChanged(normalized);
            return true;
          }
          async function persistWebDockCustomIconMap(nextMap) {
            const normalized = normalizeWebDockCustomIconMap(nextMap);
            if (serializedByteLength(normalized) > WEB_DOCK_CUSTOM_ICON_MAP_MAX_BYTES) {
              throw new Error("\u672C\u5730\u56FE\u6807\u7A7A\u95F4\u5DF2\u6EE1\uFF0C\u8BF7\u5220\u9664\u90E8\u5206\u81EA\u5B9A\u4E49\u56FE\u6807\u540E\u91CD\u8BD5");
            }
            await chrome2.storage.local.set({ [WEB_DOCK_CUSTOM_ICONS_KEY]: normalized });
            refreshWebDockCustomIcons(normalized);
            return normalized;
          }
          async function setWebDockCustomIcon(siteUrl, dataUrl) {
            const url = normalizeDockUrl(siteUrl);
            const icon = safeEphemeralDockIconUrl(dataUrl, url);
            if (!icon || !icon.startsWith("data:image/") || icon.length > WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH) {
              throw new Error("\u56FE\u7247\u538B\u7F29\u540E\u4ECD\u7136\u8FC7\u5927\uFF0C\u8BF7\u6362\u4E00\u5F20\u66F4\u7B80\u6D01\u7684\u56FE\u7247");
            }
            return persistWebDockCustomIconMap({ ...webDockCustomIcons, [url]: icon });
          }
          async function removeWebDockCustomIcon(siteUrl) {
            let url;
            try {
              url = normalizeDockUrl(siteUrl);
            } catch (_error) {
              return false;
            }
            if (!Object.prototype.hasOwnProperty.call(webDockCustomIcons, url)) return false;
            const nextMap = { ...webDockCustomIcons };
            delete nextMap[url];
            await persistWebDockCustomIconMap(nextMap);
            return true;
          }
          function compareWebDockFaviconCandidates(first, second) {
            const preferredSourceRank = (candidate) => {
              if (candidate && candidate.source === "local") return 0;
              if (candidate && candidate.source === "builtin") return 1;
              if (candidate && candidate.source === "custom") return 2;
              return 3;
            };
            const preferredDelta = preferredSourceRank(first) - preferredSourceRank(second);
            return preferredDelta || compareDockIconCandidates(first, second);
          }
          function builtInWebDockIcon(site) {
            if (!WebDockCatalog || typeof WebDockCatalog.findByUrl !== "function" || !site) return "";
            const match = WebDockCatalog.findByUrl(site.url);
            return match && match.iconFile ? chrome2.runtime.getURL(match.iconFile) : "";
          }
          function webDockFaviconSource(site) {
            if (site.iconMode === "text") return "";
            const state = webDockFaviconStates.get(site.url);
            return state ? state.committedUrl || state.selectedUrl : webDockCustomIcons[site.url] || builtInWebDockIcon(site) || site.iconUrl || chromeDockFaviconUrl(site);
          }
          function webDockFaviconIdentity(site) {
            return JSON.stringify([site.id, site.url, site.iconMode || "auto", site.iconText || "", site.iconUrl || ""]);
          }
          function createWebDockFaviconState(site) {
            const textIcon = site.iconMode === "text" ? normalizeDockIconText(site.iconText) : "";
            const localIcon = textIcon ? "" : webDockCustomIcons[site.url] || "";
            const builtInIcon = !textIcon && !localIcon ? builtInWebDockIcon(site) : "";
            const customIcon = !localIcon && !builtInIcon && site.iconMode === "custom" ? site.iconUrl || "" : "";
            const preferredIcon = localIcon || builtInIcon || customIcon;
            const preferredCandidate = preferredIcon ? {
              url: preferredIcon,
              source: localIcon ? "local" : builtInIcon ? "builtin" : "custom",
              vector: /\.svg(?:$|[?#])/i.test(preferredIcon),
              declaredSize: localIcon || builtInIcon ? 256 : 0,
              verified: true
            } : null;
            return {
              siteId: site.id,
              url: site.url,
              iconIdentity: webDockFaviconIdentity(site),
              attempt: 0,
              candidates: preferredCandidate ? [preferredCandidate] : [],
              candidateIndex: preferredCandidate ? 0 : -1,
              status: textIcon ? "fallback" : preferredCandidate ? "loading" : "pending",
              quality: textIcon ? "text" : "pending",
              selectedUrl: preferredIcon,
              candidateVersion: preferredCandidate ? 1 : 0,
              committedUrl: "",
              committedCandidate: null,
              committedQuality: "pending",
              selectedCandidate: preferredCandidate,
              triedCandidates: new Set(preferredCandidate ? [webDockFaviconCandidateKey(preferredCandidate)] : []),
              degradedCandidates: /* @__PURE__ */ new Map(),
              degradedFinalizing: false,
              generation: current().generation,
              revision: current().revision,
              sequence: 0,
              timer: null,
              watchdogTimer: null,
              probeImage: null,
              probeCleanup: null,
              revalidatingCandidateVersion: 0,
              inFlight: false,
              match: "none",
              tabStatus: ""
            };
          }
          function clearWebDockFaviconTimer(state) {
            if (!state || state.timer === null) return;
            clearTimer(state.timer);
            state.timer = null;
          }
          function clearWebDockFaviconWatchdog(state) {
            if (!state || state.watchdogTimer === null) return;
            clearTimer(state.watchdogTimer);
            state.watchdogTimer = null;
          }
          function clearWebDockFaviconStateTimers(state) {
            clearWebDockFaviconTimer(state);
            clearWebDockFaviconWatchdog(state);
            clearWebDockFaviconProbe(state);
          }
          function clearWebDockFaviconTimers() {
            for (const state of webDockFaviconStates.values()) clearWebDockFaviconStateTimers(state);
          }
          function isCurrentWebDockFaviconState(state) {
            return !destroyed && Boolean(state) && !current().recoveryCircuitOpen && isCurrentWebDockLifecycle(state.generation) && state.revision === current().revision && webDockFaviconStates.get(state.url) === state && current().config.sites.some((site) => site.id === state.siteId && site.url === state.url);
          }
          function syncWebDockFaviconStates() {
            if (destroyed) return;
            const nextStates = /* @__PURE__ */ new Map();
            for (const site of current().config.sites) {
              const existing = webDockFaviconStates.get(site.url);
              const sameIcon = existing && existing.iconIdentity === webDockFaviconIdentity(site);
              if (existing && sameIcon && existing.generation === current().generation && existing.revision === current().revision) {
                nextStates.set(site.url, existing);
              } else {
                clearWebDockFaviconStateTimers(existing);
                const next = createWebDockFaviconState(site);
                if (sameIcon && existing.committedUrl && existing.committedCandidate) Object.assign(next, { selectedCandidate: existing.committedCandidate, selectedUrl: existing.committedUrl, committedUrl: existing.committedUrl, committedCandidate: existing.committedCandidate, committedQuality: existing.committedQuality, status: "ready", quality: existing.committedQuality });
                nextStates.set(site.url, next);
              }
            }
            for (const [url, state] of webDockFaviconStates) {
              if (!nextStates.has(url)) clearWebDockFaviconStateTimers(state);
            }
            webDockFaviconStates = nextStates;
          }
          function webDockItemForFaviconState(state) {
            const siteIndex = current().config.sites.findIndex((site) => site.id === state.siteId && site.url === state.url);
            return siteIndex >= 0 ? getWebDockItems()[siteIndex] || null : null;
          }
          function applyWebDockFaviconStateToElement(state, item) {
            if (!item) return;
            const image = item.querySelector("img.web-dock-favicon");
            const fallback = item.querySelector(".dock-favicon-fallback");
            if (!image || !fallback) return;
            item.dataset.faviconState = state.status;
            item.dataset.faviconQuality = state.quality;
            const displayedUrl = state.committedUrl || state.selectedUrl;
            const hasDisplayedImage = Boolean(displayedUrl);
            fallback.hidden = hasDisplayedImage;
            fallback.style.display = hasDisplayedImage ? "none" : "";
            if (!hasDisplayedImage) {
              image.removeAttribute("src");
              image.hidden = true;
              image.style.display = "none";
              return;
            }
            if (image.getAttribute("src") !== displayedUrl) image.setAttribute("src", displayedUrl);
            image.hidden = false;
            image.style.display = "";
          }
          function applyWebDockFaviconStateToItem(state) {
            if (!isCurrentWebDockFaviconState(state)) return;
            applyWebDockFaviconStateToElement(state, webDockItemForFaviconState(state));
            const { settingsDialog, settingsSection } = current();
            if (!settingsDialog || settingsSection !== "web-dock") return;
            const preview = settingsDialog.querySelector("#dockMotionPreview");
            if (preview) {
              preview.querySelectorAll("[data-dock-preview-site]").forEach((item) => {
                if (item.dataset.dockSiteUrl === state.url) applyWebDockFaviconStateToElement(state, item);
              });
            }
            syncDockSettingsFaviconState(state);
          }
          function webDockFaviconCandidateKey(candidate) {
            return `${candidate.source}
${candidate.url}
${candidate.verified === true ? "verified" : "unverified"}`;
          }
          function rememberWebDockDegradedCandidate(state, candidate, naturalWidth, naturalHeight) {
            if (!candidate || candidate.verified !== true || candidate.vector === true) return "";
            const shortSide = Math.min(Number(naturalWidth) || 0, Number(naturalHeight) || 0);
            if (shortSide <= 0) return "";
            const key = webDockFaviconCandidateKey(candidate);
            const existing = state.degradedCandidates.get(key);
            if (!existing || shortSide > existing.shortSide) {
              state.degradedCandidates.set(key, { key, candidate, shortSide });
            }
            return key;
          }
          function bestWebDockDegradedCandidate(state) {
            return [...state.degradedCandidates.values()].sort((first, second) => second.shortSide - first.shortSide || compareWebDockFaviconCandidates(first.candidate, second.candidate) || first.key.localeCompare(second.key))[0] || null;
          }
          function finalizeWebDockFaviconCandidates(state, loadedCandidateKey = "") {
            state.candidateIndex = -1;
            state.degradedFinalizing = false;
            if (state.attempt < DOCK_FAVICON_RETRY_DELAYS.length) {
              state.selectedCandidate = null;
              state.selectedUrl = "";
              state.candidateVersion += 1;
              state.status = "pending";
              state.quality = "pending";
              applyWebDockFaviconStateToItem(state);
              return;
            }
            const best = bestWebDockDegradedCandidate(state);
            if (!best) {
              if (state.committedUrl && state.committedCandidate) {
                state.selectedCandidate = state.committedCandidate;
                state.selectedUrl = state.committedUrl;
                state.candidateVersion += 1;
                state.status = "ready";
                state.quality = state.committedQuality;
                applyWebDockFaviconStateToItem(state);
                return;
              }
              state.selectedCandidate = null;
              state.selectedUrl = "";
              state.candidateVersion += 1;
              state.committedUrl = "";
              state.committedCandidate = null;
              state.committedQuality = "fallback";
              state.status = "fallback";
              state.quality = "fallback";
              applyWebDockFaviconStateToItem(state);
              return;
            }
            state.selectedCandidate = best.candidate;
            state.selectedUrl = best.candidate.url;
            state.candidateVersion += 1;
            if (loadedCandidateKey === best.key) {
              state.status = "ready";
              state.quality = "degraded";
              clearWebDockFaviconTimer(state);
              applyWebDockFaviconStateToItem(state);
              return;
            }
            state.status = "loading";
            state.quality = "pending";
            state.degradedFinalizing = true;
            applyWebDockFaviconStateToItem(state);
            stageWebDockFaviconReplacement(state);
          }
          function mergeWebDockFaviconCandidates(state, incoming) {
            const candidatesByUrl = /* @__PURE__ */ new Map();
            for (const candidate of [...state.candidates, ...incoming]) {
              const current2 = candidatesByUrl.get(candidate.url);
              const verificationImproved = current2 && current2.verified !== true && candidate.verified === true;
              const equallyVerifiedButBetter = current2 && current2.verified === candidate.verified && compareWebDockFaviconCandidates(candidate, current2) < 0;
              if (!current2 || verificationImproved || equallyVerifiedButBetter) {
                candidatesByUrl.set(candidate.url, candidate);
              }
            }
            state.candidates = [...candidatesByUrl.values()].sort(compareWebDockFaviconCandidates);
          }
          function startWebDockFaviconWatchdog(state, image) {
            clearWebDockFaviconWatchdog(state);
            if (!isCurrentWebDockFaviconState(state) || state.status !== "loading" || !state.selectedUrl || !state.selectedCandidate || state.probeImage !== image) return;
            const generation = state.generation;
            const revision = state.revision;
            const sequence = state.sequence;
            const siteId = state.siteId;
            const siteUrl = state.url;
            const selectedUrl = state.selectedUrl;
            state.watchdogTimer = setTimer(() => {
              state.watchdogTimer = null;
              if (!isCurrentWebDockFaviconState(state) || state.generation !== generation || state.revision !== revision || state.sequence !== sequence || state.siteId !== siteId || state.url !== siteUrl || state.selectedUrl !== selectedUrl || state.status !== "loading" || state.probeImage !== image || image.getAttribute("src") !== selectedUrl)
                return;
              expireWebDockFaviconCandidate(state);
            }, DOCK_FAVICON_LOAD_TIMEOUT_MS);
          }
          function webDockFaviconLoadToken(state, image) {
            return {
              candidateVersion: state.candidateVersion,
              candidateKey: state.selectedCandidate ? webDockFaviconCandidateKey(state.selectedCandidate) : "",
              selectedUrl: state.selectedUrl,
              image
            };
          }
          function isCurrentWebDockFaviconLoadToken(state, token) {
            return isCurrentWebDockFaviconState(state) && token && state.status === "loading" && state.candidateVersion === token.candidateVersion && state.selectedUrl === token.selectedUrl && state.selectedCandidate && webDockFaviconCandidateKey(state.selectedCandidate) === token.candidateKey && state.probeImage === token.image && token.image.getAttribute("src") === token.selectedUrl;
          }
          function expireWebDockFaviconCandidate(state) {
            clearWebDockFaviconProbe(state);
            if (state.degradedFinalizing && state.selectedCandidate) {
              state.degradedCandidates.delete(webDockFaviconCandidateKey(state.selectedCandidate));
              state.degradedFinalizing = false;
            }
            advanceWebDockFaviconCandidate(state);
            if (state.status === "pending" && state.timer === null) scheduleWebDockFaviconRetry(state);
          }
          function startWebDockFaviconDecodeWatchdog(state, token) {
            clearWebDockFaviconWatchdog(state);
            if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
            state.watchdogTimer = setTimer(() => {
              state.watchdogTimer = null;
              if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
              expireWebDockFaviconCandidate(state);
            }, DOCK_FAVICON_DECODE_TIMEOUT_MS);
          }
          function stageWebDockFaviconReplacement(state) {
            if (!state.selectedUrl || !state.selectedCandidate) return null;
            clearWebDockFaviconProbe(state);
            const probe = document2.createElement("img");
            state.probeImage = probe;
            const token = webDockFaviconLoadToken(state, probe);
            const handleLoad = () => settleWebDockFaviconLoad(state, probe, token);
            const handleError = () => {
              if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
              handleWebDockFaviconError(state, probe);
            };
            state.probeCleanup = () => {
              probe.removeEventListener("load", handleLoad);
              probe.removeEventListener("error", handleError);
              if (state.probeImage === probe) state.probeImage = null;
              if (state.probeCleanup) state.probeCleanup = null;
            };
            probe.setAttribute("referrerpolicy", "no-referrer");
            probe.addEventListener("load", handleLoad, { once: true });
            probe.addEventListener("error", handleError, { once: true });
            probe.setAttribute("src", state.selectedUrl);
            monitorWebDockFaviconCandidate(state, probe, token);
            return probe;
          }
          function clearWebDockFaviconProbe(state) {
            if (!state) return;
            const cleanup = state.probeCleanup;
            if (typeof cleanup === "function") cleanup();
            else state.probeImage = null;
          }
          function monitorWebDockFaviconCandidate(state, image, token = webDockFaviconLoadToken(state, image)) {
            if (!image) return;
            if (image.complete) {
              if (image.naturalWidth > 0) settleWebDockFaviconLoad(state, image, token);
              else handleWebDockFaviconError(state, image);
              return;
            }
            startWebDockFaviconWatchdog(state, image);
          }
          function advanceWebDockFaviconCandidate(state, loadedCandidateKey = "") {
            if (!isCurrentWebDockFaviconState(state) || state.status === "ready") return;
            state.revalidatingCandidateVersion = 0;
            clearWebDockFaviconWatchdog(state);
            const nextIndex = state.candidates.findIndex((candidate2) => candidate2.verified === true && !state.triedCandidates.has(webDockFaviconCandidateKey(candidate2)));
            if (nextIndex === -1) {
              finalizeWebDockFaviconCandidates(state, loadedCandidateKey);
              return;
            }
            const candidate = state.candidates[nextIndex];
            state.triedCandidates.add(webDockFaviconCandidateKey(candidate));
            state.candidateIndex = nextIndex;
            state.selectedCandidate = candidate;
            state.selectedUrl = candidate.url;
            state.candidateVersion += 1;
            state.status = "loading";
            state.quality = "pending";
            state.degradedFinalizing = false;
            applyWebDockFaviconStateToItem(state);
            stageWebDockFaviconReplacement(state);
          }
          function handleWebDockFaviconLoad(state, image) {
            if (!isCurrentWebDockFaviconState(state) || state.status !== "loading" || state.probeImage !== image || image.getAttribute("src") !== state.selectedUrl) return;
            clearWebDockFaviconWatchdog(state);
            const requiredPixels = requiredDockFaviconPixels(current().config.motion, getDevicePixelRatio());
            if (isFinalDockFaviconCandidate(state.selectedCandidate, image.naturalWidth, image.naturalHeight, requiredPixels)) {
              state.committedUrl = state.selectedUrl;
              state.committedCandidate = state.selectedCandidate;
              state.committedQuality = "high";
              clearWebDockFaviconProbe(state);
              state.revalidatingCandidateVersion = 0;
              state.status = "ready";
              state.quality = "high";
              state.degradedFinalizing = false;
              clearWebDockFaviconTimer(state);
              applyWebDockFaviconStateToItem(state);
              return;
            }
            const degradedKey = rememberWebDockDegradedCandidate(state, state.selectedCandidate, image.naturalWidth, image.naturalHeight);
            if (degradedKey) {
              state.committedUrl = state.selectedUrl;
              state.committedCandidate = state.selectedCandidate;
              state.committedQuality = "degraded";
              clearWebDockFaviconProbe(state);
              state.revalidatingCandidateVersion = 0;
              applyWebDockFaviconStateToItem(state);
            }
            if (state.degradedFinalizing && degradedKey) {
              state.status = "ready";
              state.quality = "degraded";
              state.degradedFinalizing = false;
              clearWebDockFaviconTimer(state);
              applyWebDockFaviconStateToItem(state);
              return;
            }
            advanceWebDockFaviconCandidate(state, degradedKey);
            if (state.status === "pending" && state.timer === null) scheduleWebDockFaviconRetry(state);
          }
          async function settleWebDockFaviconLoad(state, image, token) {
            if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
            startWebDockFaviconDecodeWatchdog(state, token);
            try {
              if (typeof image.decode === "function") await image.decode();
            } catch (_error) {
              if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
              handleWebDockFaviconError(state, image);
              return;
            }
            if (!isCurrentWebDockFaviconLoadToken(state, token)) return;
            handleWebDockFaviconLoad(state, image);
          }
          function handleWebDockFaviconError(state, image) {
            if (!isCurrentWebDockFaviconState(state) || state.status !== "loading" || state.probeImage !== image || image.getAttribute("src") !== state.selectedUrl) return;
            clearWebDockFaviconWatchdog(state);
            if (state.probeImage === image) clearWebDockFaviconProbe(state);
            const isCommittedRevalidation = state.revalidatingCandidateVersion === state.candidateVersion && state.selectedUrl === state.committedUrl && state.selectedCandidate && state.committedCandidate && webDockFaviconCandidateKey(state.selectedCandidate) === webDockFaviconCandidateKey(state.committedCandidate);
            if (isCommittedRevalidation) {
              state.committedUrl = "";
              state.committedCandidate = null;
              state.committedQuality = "fallback";
            }
            state.revalidatingCandidateVersion = 0;
            if (state.degradedFinalizing && state.selectedCandidate) {
              state.degradedCandidates.delete(webDockFaviconCandidateKey(state.selectedCandidate));
              state.degradedFinalizing = false;
            }
            advanceWebDockFaviconCandidate(state);
            if (state.status === "pending" && state.timer === null) scheduleWebDockFaviconRetry(state);
          }
          function bindWebDockFaviconItem(state, item) {
            const image = item.querySelector("img.web-dock-favicon");
            if (!image) return;
            clearWebDockFaviconWatchdog(state);
            if (state.status === "ready" && state.committedUrl) {
              state.status = "loading";
              state.quality = "pending";
              state.selectedUrl = state.committedUrl;
              state.candidateVersion += 1;
              state.revalidatingCandidateVersion = state.candidateVersion;
            }
            image.dataset.dockFaviconStateBound = "true";
            applyWebDockFaviconStateToItem(state);
            if (state.status === "loading") {
              stageWebDockFaviconReplacement(state);
            }
          }
          function bindDockFaviconElements(container) {
            if (!container) return;
            container.querySelectorAll(".web-dock-item").forEach((item) => {
              const image = item.querySelector("img.web-dock-favicon");
              const fallback = item.querySelector(".dock-favicon-fallback");
              if (!image || !fallback) return;
              const showImage = () => {
                image.hidden = false;
                image.style.display = "";
                fallback.hidden = true;
                fallback.style.display = "none";
              };
              const showFallback = () => {
                image.hidden = true;
                image.style.display = "none";
                fallback.hidden = false;
                fallback.style.display = "";
              };
              if (image.dataset.dockFaviconContentBound !== "true") {
                image.dataset.dockFaviconContentBound = "true";
                image.addEventListener("load", showImage);
                image.addEventListener("error", showFallback);
              }
              const state = webDockFaviconStates.get(item.dataset.dockSiteUrl);
              if (item.dataset.dockPreviewSite === "true") {
                if (state) applyWebDockFaviconStateToElement(state, item);
                if (image.complete) {
                  if (image.naturalWidth > 0) showImage();
                  else showFallback();
                }
                return;
              }
              if (state) bindWebDockFaviconItem(state, item);
              else if (image.complete) {
                if (image.naturalWidth > 0) showImage();
                else showFallback();
              }
            });
          }
          function scheduleWebDockFaviconRetry(state) {
            if (!isCurrentWebDockFaviconState(state) || state.status !== "pending" || state.inFlight || state.attempt >= DOCK_FAVICON_RETRY_DELAYS.length) return;
            clearWebDockFaviconTimer(state);
            const delay = DOCK_FAVICON_RETRY_DELAYS[state.attempt];
            const generation = state.generation;
            const revision = state.revision;
            const sequence = state.sequence;
            state.timer = setTimer(() => {
              state.timer = null;
              if (!isCurrentWebDockFaviconState(state) || state.generation !== generation || state.revision !== revision || state.sequence !== sequence) return;
              requestWebDockFaviconResolution(state);
            }, delay);
          }
          function sanitizedWebDockFaviconCandidates(state, rawCandidates) {
            const allowedSources = /* @__PURE__ */ new Set(["local", "page-svg", "page-touch", "page-icon", "saved", "tab"]);
            const candidates = [];
            for (const rawCandidate of Array.isArray(rawCandidates) ? rawCandidates.slice(0, 12) : []) {
              if (!rawCandidate || !allowedSources.has(rawCandidate.source)) continue;
              const url = safeEphemeralDockIconUrl(rawCandidate.url, state.url);
              if (!url) continue;
              const numericSize = Number(rawCandidate.declaredSize);
              candidates.push({
                url,
                source: rawCandidate.source,
                vector: rawCandidate.vector === true,
                declaredSize: Number.isFinite(numericSize) ? Math.max(0, numericSize) : 0,
                verified: rawCandidate.verified === true
              });
            }
            return candidates;
          }
          async function requestWebDockFaviconResolution(state) {
            if (!isCurrentWebDockFaviconState(state) || state.status !== "pending" || state.inFlight || state.attempt >= DOCK_FAVICON_RETRY_DELAYS.length) return;
            clearWebDockFaviconStateTimers(state);
            state.attempt += 1;
            state.inFlight = true;
            const sequence = ++webDockFaviconRequestSequence;
            state.sequence = sequence;
            const generation = state.generation;
            const revision = state.revision;
            let response = null;
            try {
              response = await send({
                type: "DOCK_RESOLVE_FAVICONS",
                urls: [state.url],
                faviconSize: selectDockFaviconSize(current().config.motion, getDevicePixelRatio())
              });
            } catch (_) {
            }
            if (!isCurrentWebDockFaviconState(state) || state.sequence !== sequence || state.generation !== generation || state.revision !== revision) return;
            state.inFlight = false;
            let resolved = null;
            if (response && response.ok && Array.isArray(response.icons)) {
              resolved = response.icons.find((icon) => {
                if (!icon || typeof icon.url !== "string") return false;
                try {
                  return normalizeDockUrl(icon.url) === state.url;
                } catch (_) {
                  return false;
                }
              }) || null;
            }
            state.match = resolved && ["exact", "same-origin"].includes(resolved.match) ? resolved.match : "none";
            state.tabStatus = resolved && ["loading", "complete"].includes(resolved.tabStatus) ? resolved.tabStatus : "";
            const candidates = sanitizedWebDockFaviconCandidates(state, resolved && resolved.candidates);
            const site = current().config.sites.find((candidate) => candidate.id === state.siteId && candidate.url === state.url);
            const localIcon = webDockCustomIcons[state.url];
            if (localIcon) {
              candidates.push({
                url: localIcon,
                source: "local",
                vector: false,
                declaredSize: 256,
                verified: true
              });
            }
            if (site && site.iconUrl) {
              candidates.push({
                url: site.iconUrl,
                source: site.iconMode === "custom" ? "custom" : "saved",
                vector: /\.svg(?:$|[?#])/i.test(site.iconUrl),
                declaredSize: 0,
                verified: true
              });
            }
            candidates.push({
              url: chromeDockFaviconUrl(site || { url: state.url }),
              source: "chrome",
              vector: false,
              declaredSize: selectDockFaviconSize(current().config.motion, getDevicePixelRatio()),
              verified: Boolean(resolved && resolved.chromeFaviconVerified === true)
            });
            mergeWebDockFaviconCandidates(state, candidates);
            state.triedCandidates.clear();
            advanceWebDockFaviconCandidate(state);
            scheduleWebDockFaviconRetry(state);
          }
          function resolveWebDockFavicons() {
            for (const state of webDockFaviconStates.values()) {
              if (state.attempt === 0 && state.status === "pending" && !state.inFlight) {
                requestWebDockFaviconResolution(state);
              }
            }
          }
          function retryUnfinalizedWebDockFavicons() {
            for (const state of webDockFaviconStates.values()) {
              if (state.status !== "pending" || state.inFlight || state.attempt >= DOCK_FAVICON_RETRY_DELAYS.length) continue;
              clearWebDockFaviconTimer(state);
              requestWebDockFaviconResolution(state);
            }
          }
          function reset() {
            clearWebDockFaviconTimers();
            webDockFaviconRequestSequence += 1;
            webDockFaviconStates = /* @__PURE__ */ new Map();
          }
          function destroy() {
            if (destroyed) return;
            destroyed = true;
            reset();
          }
          return Object.freeze({
            loadCustomIcons: loadWebDockCustomIcons,
            refreshCustomIcons: refreshWebDockCustomIcons,
            replaceCustomIcons: persistWebDockCustomIconMap,
            setCustomIcon: setWebDockCustomIcon,
            removeCustomIcon: removeWebDockCustomIcon,
            getCustomIcon: (url) => webDockCustomIcons[url] || "",
            getCustomIcons: () => ({ ...webDockCustomIcons }),
            chromeUrl: chromeDockFaviconUrl,
            builtInIcon: builtInWebDockIcon,
            source: webDockFaviconSource,
            getState: (url) => webDockFaviconStates.get(url) || null,
            syncStates: syncWebDockFaviconStates,
            bindElements: bindDockFaviconElements,
            resolve: resolveWebDockFavicons,
            retryUnfinalized: retryUnfinalizedWebDockFavicons,
            clearTimers: clearWebDockFaviconTimers,
            reset,
            destroy
          });
        }
        return Object.freeze({ createDockFaviconRuntime });
      });
    }
  });

  // src/content/dock-authority-runtime.js
  var require_dock_authority_runtime = __commonJS({
    "src/content/dock-authority-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockAuthorityRuntime = api;
        else if (!commonJs) root.TfaDockAuthorityRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockAuthorityRuntime(deps = {}) {
          const document2 = deps.document;
          const Observer = deps.MutationObserver;
          const ownerId = String(deps.ownerId || "");
          const generation = Number.isFinite(deps.generation) ? deps.generation : 2;
          const records = /* @__PURE__ */ new Map();
          let documentObserver = null;
          let destroyed = false;
          function hostGeneration(host) {
            const value = Number(host?.getAttribute?.("data-tfa-dock-generation"));
            return Number.isFinite(value) && value >= 0 ? value : 0;
          }
          function parts(host) {
            if (!host || !host.isConnected || host.getAttribute?.("data-tfa-host") !== "true") return null;
            const foreignOwner = String(host.getAttribute?.("data-tfa-owner") || "");
            if (!foreignOwner || foreignOwner === ownerId || hostGeneration(host) >= generation) return null;
            const root = host.shadowRoot;
            if (!root) return null;
            const shell = root.getElementById?.("webDockShell");
            if (!shell || !root.getElementById?.("overlay") || !root.getElementById?.("panel")) return null;
            return { host, shell };
          }
          function enforce(record) {
            if (destroyed || !record?.shell?.isConnected) return false;
            const marker = record.shell.getAttribute?.("data-tfa-dock-suppressed-by");
            if (marker && marker !== ownerId) return false;
            record.shell.setAttribute?.("data-tfa-dock-suppressed-by", ownerId);
            if (record.shell.hidden !== true) record.shell.hidden = true;
            if (record.shell.inert !== true) record.shell.inert = true;
            if (record.shell.getAttribute?.("aria-hidden") !== "true") record.shell.setAttribute?.("aria-hidden", "true");
            if (record.shell.style && record.shell.style.pointerEvents !== "none") record.shell.style.pointerEvents = "none";
            return true;
          }
          function restore(record) {
            record?.observer?.disconnect?.();
            const shell = record?.shell;
            if (!shell || shell.getAttribute?.("data-tfa-dock-suppressed-by") !== ownerId) return false;
            shell.removeAttribute?.("data-tfa-dock-suppressed-by");
            shell.hidden = record.hidden;
            shell.inert = record.inert;
            if (record.ariaHidden == null) shell.removeAttribute?.("aria-hidden");
            else shell.setAttribute?.("aria-hidden", record.ariaHidden);
            if (shell.style) shell.style.pointerEvents = record.pointerEvents;
            return true;
          }
          function suppress(host) {
            const match = parts(host);
            if (!match) return false;
            let record = records.get(match.shell);
            if (!record) {
              record = {
                host: match.host,
                shell: match.shell,
                hidden: match.shell.hidden === true,
                inert: match.shell.inert === true,
                ariaHidden: match.shell.getAttribute?.("aria-hidden"),
                pointerEvents: match.shell.style?.pointerEvents || "",
                observer: null
              };
              records.set(match.shell, record);
            }
            if (!record.observer && typeof Observer === "function") {
              record.observer = new Observer(() => enforce(record));
              record.observer.observe(match.shell, {
                attributes: true,
                attributeFilter: ["hidden", "inert", "aria-hidden", "style"]
              });
            }
            return enforce(record);
          }
          function reconcile() {
            if (destroyed) return 0;
            const hosts = document2?.documentElement?.querySelectorAll?.('[data-tfa-host="true"]') || [];
            const eligibleShells = /* @__PURE__ */ new Set();
            for (const host of hosts) {
              const match = parts(host);
              if (!match) continue;
              eligibleShells.add(match.shell);
              suppress(host);
            }
            for (const [shell, record] of records) {
              if (eligibleShells.has(shell) && shell.isConnected) continue;
              restore(record);
              records.delete(shell);
            }
            return eligibleShells.size;
          }
          function markOwnerHost(host, metadata = {}) {
            if (destroyed || !host || host.getAttribute?.("data-tfa-owner") !== ownerId) return false;
            host.setAttribute?.("data-tfa-dock-generation", String(generation));
            if (metadata.version) host.setAttribute?.("data-tfa-version", String(metadata.version));
            if (metadata.fingerprint) host.setAttribute?.("data-tfa-fingerprint", String(metadata.fingerprint));
            reconcile();
            return true;
          }
          function start() {
            if (destroyed || documentObserver) return false;
            if (typeof Observer === "function" && document2?.documentElement) {
              documentObserver = new Observer(reconcile);
              documentObserver.observe(document2.documentElement, {
                childList: true,
                subtree: true,
                attributes: true,
                attributeFilter: ["data-tfa-host", "data-tfa-owner", "data-tfa-dock-generation"]
              });
            }
            reconcile();
            return true;
          }
          function pause() {
            if (destroyed) return false;
            documentObserver?.disconnect?.();
            documentObserver = null;
            for (const record of records.values()) {
              record.observer?.disconnect?.();
              record.observer = null;
            }
            return true;
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            documentObserver?.disconnect?.();
            documentObserver = null;
            for (const record of records.values()) restore(record);
            records.clear();
            return true;
          }
          return Object.freeze({
            start,
            pause,
            reconcile,
            markOwnerHost,
            destroy,
            inspect: () => ({ destroyed, suppressedCount: records.size })
          });
        }
        return { createDockAuthorityRuntime };
      });
    }
  });

  // src/content/dock-settings-runtime.js
  var require_dock_settings_runtime = __commonJS({
    "src/content/dock-settings-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsRuntime = api;
        else if (!commonJs) root.TfaDockSettingsRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockSettingsRuntime(deps) {
          const {
            send,
            normalizeConfig,
            normalizeColor,
            motionLimits,
            appearanceLimits,
            defaultConfig,
            context,
            beginMutation,
            applyConfig,
            applyExternalConfig,
            showToast = () => {
            },
            showError = () => {
            },
            setTimeoutFn = setTimeout,
            clearTimeoutFn = clearTimeout,
            localWriteTimeoutMs = 3e3,
            visualDelayMs = 140
          } = deps;
          let destroyed = false;
          let started = false;
          let confirmedBase = normalizeConfig(defaultConfig);
          let queue = [];
          let active = null;
          let queueSequence = 0;
          let lastConfirmedSequence = 0;
          let localWriteSequence = 0;
          const localWrites = /* @__PURE__ */ new Map();
          let visualTimer = null;
          let pendingVisual = emptyVisualPatch();
          let visualEpoch = 0;
          let visualDraft = null;
          let visualDraftRevision = 0;
          const rowDrafts = /* @__PURE__ */ new Map();
          let rowSaveSequence = 0;
          function emptyVisualPatch() {
            return { motion: {}, appearance: {} };
          }
          function rowDraftKey(identity = {}) {
            return `${identity.originalUrl || ""}
${identity.index ?? ""}`;
          }
          function updateRowDraft(identity, draft = {}) {
            if (destroyed) return null;
            const key = rowDraftKey(identity);
            const previous = rowDrafts.get(key);
            const state = {
              key,
              siteId: identity.siteId || previous?.siteId || "",
              originalUrl: identity.originalUrl || previous?.originalUrl || "",
              index: String(identity.index ?? previous?.index ?? ""),
              title: String(draft.title ?? previous?.title ?? ""),
              url: String(draft.url ?? previous?.url ?? ""),
              revision: (previous?.revision || 0) + 1,
              dirty: true,
              inFlight: previous?.inFlight || null,
              saveToken: previous?.saveToken || null
            };
            rowDrafts.set(key, state);
            return { ...state, inFlight: Boolean(state.inFlight) };
          }
          function getRowDraft(identity) {
            const state = rowDrafts.get(rowDraftKey(identity));
            return state ? { ...state, inFlight: Boolean(state.inFlight) } : null;
          }
          function getDirtyRowDrafts() {
            return Array.from(rowDrafts.values()).filter((state) => state.dirty).map((state) => ({ ...state, inFlight: Boolean(state.inFlight) }));
          }
          function clearRowDrafts() {
            rowDrafts.clear();
          }
          function saveRowDraft(identity, save) {
            if (destroyed || typeof save !== "function") return Promise.resolve({ ok: false, committed: false });
            const key = rowDraftKey(identity);
            let state = rowDrafts.get(key);
            if (!state) state = updateRowDraft(identity, identity);
            if (state.inFlight) return state.inFlight;
            const revision = state.revision;
            const token = ++rowSaveSequence;
            const snapshot = { siteId: state.siteId, title: state.title.trim(), url: state.url.trim() };
            const promise = Promise.resolve().then(() => save(snapshot)).then((result) => {
              const currentState = rowDrafts.get(key);
              const successful = Boolean(result && result.ok && result.current !== false);
              const committed = successful && currentState?.saveToken === token && currentState.revision === revision;
              if (committed) rowDrafts.delete(key);
              return { ok: successful, committed, result, draft: snapshot };
            }).finally(() => {
              const currentState = rowDrafts.get(key);
              if (currentState?.saveToken === token) {
                currentState.inFlight = null;
                currentState.saveToken = null;
              }
            });
            state.saveToken = token;
            state.inFlight = promise;
            return promise;
          }
          function clone(value) {
            return JSON.parse(JSON.stringify(value));
          }
          function same(left, right) {
            return JSON.stringify(normalizeConfig(left)) === JSON.stringify(normalizeConfig(right));
          }
          function patchConfig(config, patch) {
            return normalizeConfig({
              ...config,
              ...patch,
              motion: { ...config.motion, ...patch && patch.motion || {} },
              appearance: { ...config.appearance, ...patch && patch.appearance || {} }
            });
          }
          function current() {
            return context();
          }
          function requestIsCurrent(request, response) {
            if (destroyed || !request || !response || !response.ok || !response.config) return false;
            const value = current();
            return request.generation === value.generation && request.sequence === value.mutationSequence && (request.configRevision === value.configRevision || same(value.config, response.config));
          }
          function releaseWrite(write) {
            if (!write || !localWrites.has(write.token)) return;
            if (write.timeoutId !== null) clearTimeoutFn(write.timeoutId);
            localWrites.delete(write.token);
          }
          function finishWrite(write) {
            if (write && write.responseSeen && write.storageSeen) releaseWrite(write);
          }
          function beginWrite(operation, baseConfig) {
            const write = {
              token: ++localWriteSequence,
              expectedConfig: patchConfig(baseConfig, operation.patch),
              queueSequence: operation.queueSequence,
              configRevision: operation.request.configRevision,
              visualEpoch: Number.isInteger(operation.visualEpoch) ? operation.visualEpoch : null,
              responseSeen: false,
              storageSeen: false,
              timeoutId: null
            };
            localWrites.set(write.token, write);
            write.timeoutId = setTimeoutFn(() => releaseWrite(write), localWriteTimeoutMs);
            return write;
          }
          function observeStorage(config) {
            const write = Array.from(localWrites.values()).find((candidate) => !candidate.storageSeen && same(candidate.expectedConfig, config));
            if (!write) return null;
            write.storageSeen = true;
            finishWrite(write);
            return write;
          }
          async function sendTracked(operation, baseConfig) {
            const write = beginWrite(operation, baseConfig);
            try {
              return await send({ type: "DOCK_UPDATE_SETTINGS", patch: operation.patch });
            } finally {
              write.responseSeen = true;
              finishWrite(write);
            }
          }
          function enqueue(operation) {
            if (destroyed) return Promise.resolve(false);
            let resolve;
            const queued = {
              ...operation,
              queueSequence: ++queueSequence,
              promise: new Promise((done) => {
                resolve = done;
              }),
              resolve: null
            };
            queued.resolve = resolve;
            queue.push(queued);
            pump();
            return queued.promise;
          }
          function cancelQueued(predicate = () => true) {
            const retained = [];
            queue.forEach((operation) => {
              if (predicate(operation)) operation.resolve(false);
              else retained.push(operation);
            });
            queue = retained;
          }
          function mergeVisual(older = {}, newer = {}) {
            return {
              motion: { ...older.motion || {}, ...newer.motion || {} },
              appearance: { ...older.appearance || {}, ...newer.appearance || {} }
            };
          }
          function takeQueuedVisual(epoch) {
            let merged = emptyVisualPatch();
            const retained = [];
            queue.forEach((operation) => {
              if (operation.kind === "visual" && operation.visualEpoch === epoch) {
                merged = mergeVisual(merged, operation.patch);
                operation.resolve(false);
              } else retained.push(operation);
            });
            queue = retained;
            return merged;
          }
          function completeVisual(operation, response) {
            if (response && response.ok && response.config) {
              if (operation.visualEpoch === visualEpoch && requestIsCurrent(operation.request, response)) {
                const value = current();
                const syncDraft = value.settingsOpen && operation.draftRevision === visualDraftRevision;
                applyConfig(response.config, { syncVisualDraft: syncDraft, clearVisualError: syncDraft });
                if (value.settingsOpen) showToast("\u5916\u89C2\u4E0E\u52A8\u6548\u5DF2\u4FDD\u5B58", "success", 1200);
              }
              flushVisual();
              return true;
            }
            if (operation.visualEpoch === visualEpoch) {
              if (visualTimer !== null) clearTimeoutFn(visualTimer);
              visualTimer = null;
              pendingVisual = mergeVisual(
                mergeVisual(operation.patch, takeQueuedVisual(operation.visualEpoch)),
                pendingVisual
              );
              showError("visual", response, "\u5916\u89C2\u6216\u52A8\u6548\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
            } else flushVisual();
            return false;
          }
          function complete(operation, response) {
            if (destroyed) return false;
            if (operation.kind === "visual") return completeVisual(operation, response);
            if (operation.kind === "toggle") {
              if (response && response.ok && response.config) {
                if (requestIsCurrent(operation.request, response)) {
                  applyConfig(response.config);
                  showToast("\u7F51\u9875\u62D3\u5C55\u575E\u8BBE\u7F6E\u5DF2\u4FDD\u5B58", "success", 1500);
                }
                return true;
              }
              showError("settings", response, "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
              return false;
            }
            if (operation.kind === "reset") {
              if (response && response.ok && response.config) {
                const value = current();
                const responseIsCurrent = requestIsCurrent(operation.request, response) || same(value.config, response.config);
                if (operation.visualEpoch === visualEpoch && responseIsCurrent) {
                  const syncDraft = value.settingsOpen && operation.draftRevision === visualDraftRevision;
                  applyConfig(response.config, { syncVisualDraft: syncDraft, clearVisualError: syncDraft });
                  if (value.settingsOpen) showToast("\u9ED8\u8BA4\u53C2\u6570\u5DF2\u6062\u590D", "success", 1400);
                }
                return true;
              }
              if (operation.visualEpoch === visualEpoch) {
                showError("visual", response, "\u6062\u590D\u9ED8\u8BA4\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
              }
            }
            return false;
          }
          async function pump() {
            if (destroyed || active || !queue.length) return;
            const operation = queue.shift();
            active = operation;
            operation.request = beginMutation();
            const base = confirmedBase;
            let response;
            try {
              response = await sendTracked(operation, base);
            } catch (_error) {
              response = { ok: false, message: "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" };
            }
            if (destroyed) {
              active = null;
              operation.resolve(false);
              return;
            }
            if (response && response.ok && response.config && (operation.request.configRevision === current().configRevision || same(current().config, response.config))) {
              confirmedBase = normalizeConfig(response.config);
              lastConfirmedSequence = Math.max(lastConfirmedSequence, operation.queueSequence);
            }
            const result = complete(operation, response);
            active = null;
            operation.resolve(result);
            pump();
          }
          function sanitizeVisual(visualPatch) {
            const config = current().config;
            const patch = {};
            const motion = {};
            Object.entries(visualPatch && visualPatch.motion || {}).forEach(([key, value]) => {
              const limits = motionLimits[key];
              if (!limits) return;
              const numeric = Number(value);
              motion[key] = Math.min(limits[1], Math.max(limits[0], Number.isFinite(numeric) ? numeric : config.motion[key]));
            });
            if (Object.keys(motion).length) patch.motion = motion;
            const appearance = {};
            Object.entries(visualPatch && visualPatch.appearance || {}).forEach(([key, value]) => {
              if (key === "tileColor") {
                appearance.tileColor = normalizeColor(value, config.appearance.tileColor);
                return;
              }
              const limits = appearanceLimits[key];
              if (!limits) return;
              const numeric = Number(value);
              appearance[key] = Math.min(limits[1], Math.max(limits[0], Number.isFinite(numeric) ? numeric : config.appearance[key]));
            });
            if (Object.keys(appearance).length) patch.appearance = appearance;
            return patch;
          }
          function persistVisual(visualPatch, draftRevision = visualDraftRevision) {
            const patch = sanitizeVisual(visualPatch);
            if (!Object.keys(patch).length || destroyed) return Promise.resolve(false);
            const previous = queue[queue.length - 1];
            if (previous && previous.kind === "visual" && previous.visualEpoch === visualEpoch) {
              previous.patch = mergeVisual(previous.patch, patch);
              previous.draftRevision = draftRevision;
              return previous.promise;
            }
            return enqueue({ kind: "visual", patch, visualEpoch, draftRevision });
          }
          function queueVisual(group, key, value) {
            if (destroyed) return;
            pendingVisual = { ...pendingVisual, [group]: { ...pendingVisual[group], [key]: value } };
            if (visualTimer !== null) clearTimeoutFn(visualTimer);
            visualTimer = setTimeoutFn(() => {
              visualTimer = null;
              flushVisual();
            }, visualDelayMs);
          }
          function flushVisual() {
            if (destroyed) return Promise.resolve(false);
            if (visualTimer !== null) clearTimeoutFn(visualTimer);
            visualTimer = null;
            const pending = pendingVisual;
            pendingVisual = emptyVisualPatch();
            const patch = {};
            if (Object.keys(pending.motion).length) patch.motion = pending.motion;
            if (Object.keys(pending.appearance).length) patch.appearance = pending.appearance;
            return Object.keys(patch).length ? persistVisual(patch) : Promise.resolve(false);
          }
          function cancelVisual() {
            if (visualTimer !== null) clearTimeoutFn(visualTimer);
            visualTimer = null;
            pendingVisual = emptyVisualPatch();
            visualEpoch += 1;
            cancelQueued((operation) => operation.kind === "visual");
          }
          function updateSetting(key, value) {
            return enqueue({ kind: "toggle", patch: { [key]: value } });
          }
          function resetVisual(draftRevision = visualDraftRevision) {
            cancelVisual();
            return enqueue({
              kind: "reset",
              patch: { motion: { ...defaultConfig.motion }, appearance: { ...defaultConfig.appearance } },
              visualEpoch,
              draftRevision
            });
          }
          function onConfigApplied(config) {
            confirmedBase = normalizeConfig(config);
          }
          function syncVisualDraft(config = current().config) {
            const normalized = normalizeConfig(config);
            visualDraft = { motion: { ...normalized.motion }, appearance: { ...normalized.appearance } };
            visualDraftRevision += 1;
            return visualDraft;
          }
          function ensureVisualDraft() {
            return visualDraft || syncVisualDraft();
          }
          function updateVisualDraft(group, key, value) {
            const draft = ensureVisualDraft();
            visualDraft = { ...draft, [group]: { ...draft[group], [key]: value } };
            visualDraftRevision += 1;
            return visualDraft;
          }
          function clearVisualDraft() {
            visualDraft = null;
            visualDraftRevision += 1;
          }
          function getVisualDraft() {
            return visualDraft;
          }
          function getVisualDraftRevision() {
            return visualDraftRevision;
          }
          function handleStorageChange(config) {
            if (destroyed) return { handled: false };
            const normalized = normalizeConfig(config);
            const localWrite = observeStorage(normalized);
            const unchanged = same(current().config, normalized);
            if (localWrite) {
              if (unchanged) confirmedBase = normalized;
              const currentWrite = localWrite.configRevision === current().configRevision && (localWrite.visualEpoch === null || localWrite.visualEpoch === visualEpoch) && localWrite.queueSequence >= lastConfirmedSequence;
              if (!unchanged && currentWrite) applyConfig(normalized);
              return { handled: true, local: true, applied: !unchanged && currentWrite };
            }
            cancelVisual();
            confirmedBase = normalized;
            applyExternalConfig(normalized, { unchanged });
            return { handled: true, local: false, applied: !unchanged };
          }
          function applyBroadcast(message) {
            if (destroyed || !message || !message.config) return false;
            const normalized = normalizeConfig(message.config);
            if (same(current().config, normalized)) {
              confirmedBase = normalized;
              return false;
            }
            cancelVisual();
            confirmedBase = normalized;
            applyExternalConfig(normalized, { broadcast: true });
            return true;
          }
          function start(config = current().config) {
            if (destroyed) return false;
            confirmedBase = normalizeConfig(config || defaultConfig);
            started = true;
            return true;
          }
          function destroy() {
            if (destroyed) return;
            destroyed = true;
            started = false;
            if (visualTimer !== null) clearTimeoutFn(visualTimer);
            visualTimer = null;
            pendingVisual = emptyVisualPatch();
            visualEpoch += 1;
            visualDraft = null;
            visualDraftRevision += 1;
            rowDrafts.clear();
            cancelQueued();
            if (active) active.resolve(false);
            active = null;
            Array.from(localWrites.values()).forEach(releaseWrite);
          }
          function inspect() {
            return {
              destroyed,
              started,
              queueLength: queue.length,
              active: Boolean(active),
              localWrites: localWrites.size,
              visualEpoch,
              visualDraftRevision,
              pendingVisual: clone(pendingVisual),
              confirmedBase: clone(confirmedBase),
              rowDrafts: getDirtyRowDrafts()
            };
          }
          return {
            start,
            destroy,
            updateSetting,
            queueVisual,
            flushVisual,
            cancelVisual,
            resetVisual,
            persistVisual,
            onConfigApplied,
            syncVisualDraft,
            ensureVisualDraft,
            updateVisualDraft,
            clearVisualDraft,
            getVisualDraft,
            getVisualDraftRevision,
            updateRowDraft,
            getRowDraft,
            getDirtyRowDrafts,
            clearRowDrafts,
            saveRowDraft,
            handleStorageChange,
            applyBroadcast,
            inspect
          };
        }
        return { createDockSettingsRuntime };
      });
    }
  });

  // src/content/dock-settings-preview.js
  var require_dock_settings_preview = __commonJS({
    "src/content/dock-settings-preview.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsPreview = api;
        else if (!commonJs) root.TfaDockSettingsPreview = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockSettingsPreview(deps) {
          const {
            context,
            computeTargets,
            stepSpring,
            createSpringState,
            writeTransform,
            prefersReducedMotion,
            normalizeColor,
            requestFrame = requestAnimationFrame,
            cancelFrame = cancelAnimationFrame,
            now = () => Date.now(),
            clearanceGap = 8,
            bottomGap = 18
          } = deps;
          let destroyed = false;
          const animation = { pointerX: null, centers: [], springs: [], rafId: null, lastFrame: null, container: null };
          function items() {
            return animation.container ? Array.from(animation.container.querySelectorAll(".web-dock-item")) : [];
          }
          function visual() {
            const value = context();
            return value.visualDraft || { motion: { ...value.config.motion }, appearance: { ...value.config.appearance } };
          }
          function surface(appearance) {
            const color = normalizeColor(appearance && appearance.tileColor);
            const opacity = Math.min(100, Math.max(0, Number(appearance && appearance.tileOpacity) || 0));
            const red = Number.parseInt(color.slice(1, 3), 16);
            const green = Number.parseInt(color.slice(3, 5), 16);
            const blue = Number.parseInt(color.slice(5, 7), 16);
            return `rgba(${red}, ${green}, ${blue}, ${(opacity / 100).toFixed(2)})`;
          }
          function clearance(motion, appearance) {
            return Math.ceil(appearance.tileSize * Math.max(0, motion.magnification - 1) * 1.58 + clearanceGap);
          }
          function applyVariables() {
            const preview = animation.container;
            if (!preview) return;
            const { motion, appearance } = visual();
            preview.style.setProperty("--dock-icon-size", `${motion.iconSize}px`);
            preview.style.setProperty("--dock-tile-size", `${appearance.tileSize}px`);
            preview.style.setProperty("--dock-tray-padding", `${appearance.trayPadding}px`);
            preview.style.setProperty("--dock-tray-surface", surface(appearance));
            preview.style.setProperty("--dock-tray-fallback", surface(appearance));
            preview.style.setProperty("--dock-magnification", String(motion.magnification));
            preview.style.setProperty("--dock-gap", `${Math.max(6, Math.round(appearance.tileSize * 0.14))}px`);
            const clear = clearance(motion, appearance);
            preview.style.setProperty("--dock-preview-clearance", `${clear}px`);
            preview.style.setProperty("--dock-preview-min-height", `${clear + appearance.tileSize + appearance.trayPadding * 2 + bottomGap}px`);
          }
          function measure() {
            animation.centers = items().map((item, index) => {
              const rect = item.getBoundingClientRect();
              const state = animation.springs[index] || createSpringState();
              return rect.left + rect.width / 2 - state.x.value;
            });
          }
          function stop() {
            if (animation.rafId !== null) cancelFrame(animation.rafId);
            animation.pointerX = null;
            animation.centers = [];
            animation.springs = [];
            animation.rafId = null;
            animation.lastFrame = null;
            animation.container = null;
          }
          function request() {
            if (destroyed || animation.rafId !== null || !animation.container) return;
            let frameId = null;
            frameId = requestFrame((timestamp) => {
              if (animation.rafId === frameId) animation.rafId = null;
              run(timestamp);
            });
            animation.rafId = frameId;
          }
          function run(timestamp) {
            const preview = animation.container;
            const value = context();
            if (destroyed || !preview || value.section !== "web-dock" || value.dialog?.querySelector("#dockMotionPreview") !== preview) {
              stop();
              return;
            }
            const currentItems = items();
            if (!currentItems.length || currentItems.length !== animation.centers.length) {
              stop();
              return;
            }
            const { motion, appearance } = visual();
            const dt = animation.lastFrame === null ? 1 / 60 : Math.max(1 / 240, Math.min(1 / 30, (timestamp - animation.lastFrame) / 1e3));
            animation.lastFrame = timestamp;
            const targets = computeTargets(animation.centers, animation.pointerX, motion, appearance);
            const reduced = prefersReducedMotion();
            let settled = true;
            let activeIndex = -1;
            let activeScale = -Infinity;
            currentItems.forEach((item, index) => {
              const target = targets[index] || { scale: 1, x: 0, y: 0 };
              let state = animation.springs[index] || createSpringState();
              if (reduced) state = { scale: { value: target.scale, velocity: 0 }, x: { value: target.x, velocity: 0 }, y: { value: target.y, velocity: 0 } };
              else ["scale", "x", "y"].forEach((channel) => {
                state[channel] = stepSpring(state[channel], target[channel], dt, motion);
                if (Math.abs(state[channel].value - target[channel]) >= 1e-3 || Math.abs(state[channel].velocity) >= 1e-3) settled = false;
              });
              animation.springs[index] = state;
              writeTransform(item, { scale: state.scale.value, x: state.x.value, y: state.y.value });
              if (animation.pointerX !== null && state.scale.value > activeScale) {
                activeScale = state.scale.value;
                activeIndex = index;
              }
            });
            currentItems.forEach((item, index) => {
              if (index === activeIndex) item.dataset.dockActive = "true";
              else item.removeAttribute("data-dock-active");
            });
            if (reduced || settled) {
              currentItems.forEach((item, index) => {
                const target = targets[index] || { scale: 1, x: 0, y: 0 };
                animation.springs[index] = { scale: { value: target.scale, velocity: 0 }, x: { value: target.x, velocity: 0 }, y: { value: target.y, velocity: 0 } };
                writeTransform(item, target);
              });
              animation.lastFrame = null;
            } else request();
          }
          function start() {
            if (!animation.container || destroyed) return;
            if (prefersReducedMotion()) {
              if (animation.rafId !== null) cancelFrame(animation.rafId);
              animation.rafId = null;
              animation.lastFrame = null;
              run(now());
            } else request();
          }
          function initialize(preview) {
            stop();
            if (destroyed || !preview || context().section !== "web-dock") return false;
            animation.container = preview;
            animation.springs = items().map(() => createSpringState());
            applyVariables();
            measure();
            items().forEach((item) => writeTransform(item, { scale: 1, x: 0, y: 0 }));
            return true;
          }
          function render() {
            if (animation.container) {
              applyVariables();
              measure();
              start();
            }
          }
          function pointerMove(clientX) {
            animation.pointerX = clientX;
            measure();
            start();
          }
          function pointerLeave() {
            animation.pointerX = null;
            start();
          }
          function isConnectedTo(preview) {
            return animation.container === preview;
          }
          function destroy() {
            if (!destroyed) {
              destroyed = true;
              stop();
            }
          }
          function inspect() {
            return { destroyed, rafId: animation.rafId, pointerX: animation.pointerX, connected: Boolean(animation.container) };
          }
          return { initialize, render, stop, pointerMove, pointerLeave, isConnectedTo, destroy, inspect, surface, clearance };
        }
        return { createDockSettingsPreview };
      });
    }
  });

  // src/content/dock-settings-view-markup.js
  var require_dock_settings_view_markup = __commonJS({
    "src/content/dock-settings-view-markup.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsViewMarkup = api;
        else if (!commonJs) root.TfaDockSettingsViewMarkup = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockSettingsViewMarkup(deps) {
          const { escapeHtml, normalizeDockColor, dockFaviconRuntime, dockSettingsFaviconMarkup, translate = () => "" } = deps;
          const tr = (key, fallback, params = {}) => {
            const value = translate(key, params);
            return String(value && value !== key ? value : fallback).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          function dockSiteRowMarkup(site, index) {
            const id = escapeHtml(site.id);
            const url = escapeHtml(site.url);
            const titleId = `dockSiteTitle_${index}`;
            const urlId = `dockSiteUrl_${index}`;
            const faviconQuality = dockFaviconRuntime.getState(site.url)?.quality || "pending";
            return `<div class="dock-site-row" data-dock-site-id="${id}" data-dock-site-url="${url}" data-dock-site-index="${index}" data-favicon-quality="${escapeHtml(faviconQuality)}"><span class="dock-site-drag-handle" aria-label="${escapeHtml(tr("dock.settings.drag", "\u62D6\u52A8\u6392\u5E8F"))}" title="${escapeHtml(tr("dock.settings.drag", "\u62D6\u52A8\u6392\u5E8F"))}">\u22EE\u22EE</span>` + dockSettingsFaviconMarkup(site) + `<label class="sr-only" for="${titleId}">${escapeHtml(tr("dock.settings.siteName", "\u7F51\u7AD9\u540D\u79F0"))} ${index + 1}</label><input id="${titleId}" data-dock-site-title type="text" value="${escapeHtml(site.title)}" aria-label="${escapeHtml(tr("dock.settings.siteName", "\u7F51\u7AD9\u540D\u79F0"))} ${index + 1}"><label class="sr-only" for="${urlId}">${escapeHtml(tr("dock.settings.siteUrl", "\u7F51\u7AD9\u5730\u5740"))} ${index + 1}</label><input id="${urlId}" data-dock-site-url type="text" value="${escapeHtml(site.url)}" aria-label="${escapeHtml(tr("dock.settings.siteUrl", "\u7F51\u7AD9\u5730\u5740"))} ${index + 1}"><button type="button" data-dock-site-save>${escapeHtml(tr("dock.settings.save", "\u4FDD\u5B58"))}</button><button type="button" data-dock-site-delete>${escapeHtml(tr("dock.settings.delete", "\u5220\u9664"))}</button><div class="dock-site-inline-error" data-dock-site-error role="alert" hidden></div></div>`;
          }
          function motionStep(key) {
            if (key === "magnification") return 0.05;
            if (key === "response" || key === "damping") return 0.01;
            if (key === "influenceRadius") return 5;
            if (key === "hideDelay") return 50;
            return 1;
          }
          function formatDockAppearanceOutput(key, value) {
            return key === "tileOpacity" ? `${value}%` : String(value);
          }
          function dockTileSurfaceValue(appearance) {
            const color = normalizeDockColor(appearance && appearance.tileColor);
            const opacity = Math.min(100, Math.max(0, Number(appearance && appearance.tileOpacity) || 0));
            const channels = [1, 3, 5].map((start) => Number.parseInt(color.slice(start, start + 2), 16));
            return `rgba(${channels.join(", ")}, ${(opacity / 100).toFixed(2)})`;
          }
          function applyDockTileAppearanceVariables(element, appearance) {
            if (!element) return;
            const surface = dockTileSurfaceValue(appearance);
            const color = normalizeDockColor(appearance && appearance.tileColor);
            const channels = [1, 3, 5].map((start) => Number.parseInt(color.slice(start, start + 2), 16));
            const lightSurface = channels[0] * 0.299 + channels[1] * 0.587 + channels[2] * 0.114 >= 160;
            element.style.setProperty("--dock-tray-surface", surface);
            element.style.setProperty("--dock-tray-fallback", surface);
            element.style.setProperty("--dock-tray-text", lightSurface ? "#0f172a" : "#f4f4f8");
            element.style.setProperty("--dock-tray-muted", lightSurface ? "#64748b" : "#a8b0c5");
          }
          function rowIdentity(row) {
            return {
              siteId: row.dataset.dockSiteId || "",
              originalUrl: row.dataset.dockSiteStateUrl || row.dataset.dockSiteUrl || "",
              index: row.dataset.dockSiteIndex || ""
            };
          }
          function findRow(dialog, identity) {
            if (!dialog || !identity) return null;
            return Array.from(dialog.querySelectorAll("[data-dock-site-id]")).find((row) => row.dataset.dockSiteIndex === String(identity.index) && (row.dataset.dockSiteStateUrl === identity.originalUrl || row.dataset.dockSiteUrl === identity.originalUrl || row.dataset.dockSiteId === identity.siteId)) || null;
          }
          function aiSettingsMarkup(settings = {}, tr2 = (_key, fallback) => fallback) {
            const enabled = settings.enabled === true;
            const autoOrganize = settings.autoOrganize !== false;
            const hasApiKey = settings.hasApiKey === true;
            const baseUrl = escapeHtml(settings.baseUrl || "https://fast.sbbbbbbbbb.xyz/v1");
            const model = escapeHtml(settings.model || "grok-4.5");
            const keyPlaceholder = hasApiKey ? tr2("ai.settings.keyPreserved", "\u5DF2\u4FDD\u5B58\u5BC6\u94A5\uFF1B\u7559\u7A7A\u5C06\u7EE7\u7EED\u4F7F\u7528") : tr2("ai.settings.keyPlaceholder", "\u8F93\u5165 API Key");
            const enabledTitle = tr2("ai.settings.enabled", "\u542F\u7528 AI \u667A\u80FD\u6574\u7406");
            const autoTitle = tr2("ai.settings.autoOrganize", "\u5141\u8BB8\u9AD8\u7F6E\u4FE1\u7ED3\u679C\u81EA\u52A8\u6574\u7406");
            const showKey = tr2("ai.settings.showKey", "\u663E\u793A\u5BC6\u94A5");
            const clearKey = tr2("ai.settings.clearKey", "\u6E05\u9664\u5BC6\u94A5");
            const testConnection = tr2("ai.settings.testConnection", "\u6D4B\u8BD5\u8FDE\u63A5");
            const save = tr2("ai.settings.save", "\u4FDD\u5B58 AI \u8BBE\u7F6E");
            return `<div class="ai-settings" data-ai-settings><div class="ai-settings-state is-loading" data-ai-loading role="status"${settings.loading ? "" : " hidden"}>${escapeHtml(tr2("ai.settings.loading", "\u6B63\u5728\u8BFB\u53D6\u672C\u673A\u8BBE\u7F6E\u2026"))}</div><div class="ai-settings-state is-error" data-ai-error role="alert" hidden></div><div class="ai-settings-state is-success" data-ai-status role="status" aria-live="polite" hidden></div><section class="ai-settings-policy"><strong>${escapeHtml(tr2("ai.settings.scopeTitle", "AI \u53EA\u6574\u7406\u865A\u62DF\u4EFB\u52A1"))}</strong><p>${escapeHtml(tr2("ai.settings.scope", "AI \u53EF\u4EE5\u81EA\u52A8\u4FEE\u6539\u53F3\u4FA7\u865A\u62DF\u4EFB\u52A1\u5173\u7CFB\u548C AI \u7BA1\u7406\u7684\u4EFB\u52A1\uFF0C\u4F46\u7EDD\u4E0D\u6253\u5F00\u3001\u5173\u95ED\u3001\u79FB\u52A8\u3001\u5206\u7EC4\u6216\u91CD\u6392\u771F\u5B9E\u9875\u7B7E\u3002"))}</p></section><section class="ai-settings-switches"><button type="button" class="ai-settings-switch" data-ai-enabled role="switch" aria-checked="${enabled}" aria-label="${escapeHtml(enabledTitle)}"><span class="dock-switch-copy"><strong>${escapeHtml(enabledTitle)}</strong><small>${escapeHtml(tr2("ai.settings.enabledHint", "\u5173\u95ED\u65F6\u4E0D\u4F1A\u4EA7\u751F\u4E91\u7AEF\u8BF7\u6C42\uFF0C\u672C\u5730\u5EFA\u8BAE\u4ECD\u53EF\u4F7F\u7528"))}</small></span><span class="dock-switch-track" aria-hidden="true"><span class="dock-switch-thumb"></span></span></button><button type="button" class="ai-settings-switch" data-ai-auto-organize role="switch" aria-checked="${autoOrganize}" aria-label="${escapeHtml(autoTitle)}"><span class="dock-switch-copy"><strong>${escapeHtml(autoTitle)}</strong><small>${escapeHtml(tr2("ai.settings.autoOrganizeHint", "\u53EA\u5904\u7406\u901A\u8FC7\u672C\u5730\u5B89\u5168\u95E8\u69DB\u7684\u9AD8\u7F6E\u4FE1\u865A\u62DF\u4EFB\u52A1\u53D8\u5316"))}</small></span><span class="dock-switch-track" aria-hidden="true"><span class="dock-switch-thumb"></span></span></button></section><section class="ai-settings-channel"><header><div><h3>${escapeHtml(tr2("ai.settings.channelTitle", "\u6A21\u578B\u6E20\u9053"))}</h3><p>${escapeHtml(tr2("ai.settings.channelHint", "OpenAI \u517C\u5BB9\u63A5\u53E3\uFF1B\u5730\u5740\u548C\u6A21\u578B\u53EF\u4EE5\u4FEE\u6539"))}</p></div><span>${escapeHtml(tr2("ai.settings.provider", "OpenAI \u517C\u5BB9"))}</span></header><label for="aiSettingsBaseUrl">${escapeHtml(tr2("ai.settings.baseUrl", "API \u5730\u5740"))}</label><input id="aiSettingsBaseUrl" data-ai-base-url type="url" value="${baseUrl}" maxlength="2048" inputmode="url" autocomplete="url" spellcheck="false"><label for="aiSettingsModel">${escapeHtml(tr2("ai.settings.model", "\u6A21\u578B"))}</label><input id="aiSettingsModel" data-ai-model type="text" value="${model}" maxlength="200" autocomplete="off" spellcheck="false"><label for="aiSettingsApiKey">${escapeHtml(tr2("ai.settings.apiKey", "API Key"))}</label><div class="ai-settings-key"><input id="aiSettingsApiKey" data-ai-api-key type="password" value="" maxlength="4096" placeholder="${escapeHtml(keyPlaceholder)}" autocomplete="new-password" spellcheck="false"><button type="button" data-ai-toggle-key aria-pressed="false" aria-label="${escapeHtml(showKey)}" title="${escapeHtml(showKey)}">${escapeHtml(showKey)}</button><button type="button" data-ai-clear-key${hasApiKey ? "" : " disabled"} aria-label="${escapeHtml(clearKey)}" title="${escapeHtml(clearKey)}">${escapeHtml(clearKey)}</button></div><p class="ai-settings-local-note">${escapeHtml(tr2("ai.settings.localKey", "\u5BC6\u94A5\u4EC5\u4FDD\u5B58\u5728\u672C\u673A\uFF0C\u4E0D\u540C\u6B65\u3001\u4E0D\u5BFC\u51FA\uFF0C\u4E5F\u4E0D\u4F1A\u51FA\u73B0\u5728\u65E5\u5FD7\u6216\u63D0\u793A\u6D88\u606F\u4E2D\u3002"))}</p></section><details class="ai-settings-preview"><summary>${escapeHtml(tr2("ai.settings.previewTitle", "\u67E5\u770B\u53D1\u9001\u5B57\u6BB5\u9884\u89C8"))}</summary><p>${escapeHtml(tr2("ai.settings.previewHint", "\u5F00\u542F AI \u540E\uFF0C\u4EC5\u53D1\u9001\u672C\u5730\u7B5B\u9009\u51FA\u7684\u6700\u5C0F\u5019\u9009\u4FE1\u606F\u3002"))}</p><dl><div><dt>${escapeHtml(tr2("ai.settings.previewPage", "\u5F53\u524D\u9875\u9762"))}</dt><dd>${escapeHtml(tr2("ai.settings.previewPageFields", "\u8131\u654F\u6807\u9898\u3001\u6CE8\u518C\u57DF\u540D\u3001\u8DEF\u5F84\u7C7B\u578B"))}</dd></div><div><dt>${escapeHtml(tr2("ai.settings.previewTasks", "\u5019\u9009\u4EFB\u52A1"))}</dt><dd>${escapeHtml(tr2("ai.settings.previewTaskFields", "\u4EFB\u52A1\u540D\u79F0\u3001\u9886\u57DF\u3001\u5C11\u91CF\u8131\u654F\u6838\u5FC3\u9875\u6807\u9898"))}</dd></div><div><dt>${escapeHtml(tr2("ai.settings.previewSignals", "\u672C\u5730\u4FE1\u53F7"))}</dt><dd>${escapeHtml(tr2("ai.settings.previewSignalFields", "\u805A\u5408\u540E\u7684\u8BC1\u636E\u4EE3\u7801\u3001\u98CE\u9669\u4EE3\u7801\u548C\u6B21\u6570"))}</dd></div></dl><strong>${escapeHtml(tr2("ai.settings.previewNever", "\u4E0D\u4F1A\u53D1\u9001\u5B8C\u6574 URL\u3001\u67E5\u8BE2\u53C2\u6570\u3001\u9875\u9762\u6B63\u6587\u3001\u8F93\u5165\u5185\u5BB9\u3001Cookie\u3001\u622A\u56FE\u3001\u539F\u59CB\u884C\u4E3A\u8F68\u8FF9\u6216 API Key\u3002"))}</strong></details><footer class="ai-settings-actions"><span data-ai-action-hint>${escapeHtml(tr2("ai.settings.actionHint", "\u4FEE\u6539\u540E\u5148\u6D4B\u8BD5\u8FDE\u63A5\uFF0C\u518D\u4FDD\u5B58\u8BBE\u7F6E"))}</span><button type="button" data-ai-test>${escapeHtml(testConnection)}</button><button type="button" class="dock-settings-save" data-ai-save>${escapeHtml(save)}</button></footer></div>`;
          }
          return {
            dockSiteRowMarkup,
            motionStep,
            formatDockAppearanceOutput,
            applyDockTileAppearanceVariables,
            rowIdentity,
            findRow,
            aiSettingsMarkup
          };
        }
        return { createDockSettingsViewMarkup };
      });
    }
  });

  // src/content/dock-settings-ai-section.js
  var require_dock_settings_ai_section = __commonJS({
    "src/content/dock-settings-ai-section.js"(exports, module) {
      (function attachDockSettingsAiSection(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsAiSection = api;
        else if (!commonJs) root.TfaDockSettingsAiSection = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function createDockSettingsAiSectionModule() {
        "use strict";
        function createDockSettingsAiSection(deps = {}) {
          const {
            getDialog,
            isActive,
            getLifecycle,
            isDestroyed,
            aiSettingsMarkup,
            tr,
            loadAiSettings,
            saveAiSettings,
            testAiConnection,
            requestAiOriginPermission
          } = deps;
          let aiSettings = null;
          let aiLoading = false;
          let aiSaving = false;
          let aiTesting = false;
          let aiError = "";
          let aiStatus = "";
          let aiApiKeyAction = "preserve";
          let aiLoadRequest = 0;
          function activeDialog() {
            const dialog = typeof getDialog === "function" ? getDialog() : null;
            return dialog && (typeof isActive !== "function" || isActive()) ? dialog : null;
          }
          function stale(epoch) {
            return typeof isDestroyed === "function" && isDestroyed() || typeof getLifecycle === "function" && epoch !== getLifecycle() || typeof isActive === "function" && !isActive();
          }
          function normalizeAiSettingsForView(value = {}) {
            const source = value && typeof value === "object" ? value : {};
            return {
              enabled: source.enabled === true,
              autoOrganize: source.autoOrganize !== false,
              provider: "openai-compatible",
              baseUrl: typeof source.baseUrl === "string" && source.baseUrl.trim() ? source.baseUrl.trim() : "https://fast.sbbbbbbbbb.xyz/v1",
              model: typeof source.model === "string" && source.model.trim() ? source.model.trim() : "grok-4.5",
              excludeSensitiveSites: source.excludeSensitiveSites !== false,
              hasApiKey: source.hasApiKey === true
            };
          }
          function safeUpstreamReason(value) {
            return String(value || "").replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 240);
          }
          function aiFailure(kind, code = "", upstreamReason = "") {
            const normalized = String(code || "").toUpperCase();
            const keys = {
              AI_PERMISSION_DENIED: ["ai.settings.error.permission", "\u6CA1\u6709\u83B7\u5F97\u8BE5\u5730\u5740\u7684\u8BBF\u95EE\u6743\u9650\uFF1B\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002"],
              AI_UNAUTHORIZED: ["ai.settings.error.unauthorized", "\u5BC6\u94A5\u88AB\u6E20\u9053\u62D2\u7EDD\uFF0C\u8BF7\u68C0\u67E5\u6216\u66F4\u6362\u5BC6\u94A5\u3002"],
              AUTH: ["ai.settings.error.unauthorized", "\u5BC6\u94A5\u88AB\u6E20\u9053\u62D2\u7EDD\uFF0C\u8BF7\u68C0\u67E5\u6216\u66F4\u6362\u5BC6\u94A5\u3002"],
              AI_PAYMENT_REQUIRED: ["ai.settings.error.paymentRequired", "\u6E20\u9053\u4F59\u989D\u6216\u914D\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6E20\u9053\u8D26\u6237\u3002"],
              PAYMENT_REQUIRED: ["ai.settings.error.paymentRequired", "\u6E20\u9053\u4F59\u989D\u6216\u914D\u989D\u4E0D\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6E20\u9053\u8D26\u6237\u3002"],
              AI_FORBIDDEN: ["ai.settings.error.forbidden", "\u6E20\u9053\u62D2\u7EDD\u8BBF\u95EE\u5F53\u524D\u63A5\u53E3\u6216\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5\u8D26\u6237\u548C\u6A21\u578B\u6743\u9650\u3002"],
              FORBIDDEN: ["ai.settings.error.forbidden", "\u6E20\u9053\u62D2\u7EDD\u8BBF\u95EE\u5F53\u524D\u63A5\u53E3\u6216\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5\u8D26\u6237\u548C\u6A21\u578B\u6743\u9650\u3002"],
              AI_BAD_REQUEST: ["ai.settings.error.badRequest", "\u6E20\u9053\u4E0D\u63A5\u53D7\u5F53\u524D\u6A21\u578B\u6216\u8BF7\u6C42\u53C2\u6570\uFF0C\u8BF7\u6838\u5BF9\u6A21\u578B\u540D\u79F0\u3002"],
              BAD_REQUEST: ["ai.settings.error.badRequest", "\u6E20\u9053\u4E0D\u63A5\u53D7\u5F53\u524D\u6A21\u578B\u6216\u8BF7\u6C42\u53C2\u6570\uFF0C\u8BF7\u6838\u5BF9\u6A21\u578B\u540D\u79F0\u3002"],
              AI_NOT_FOUND: ["ai.settings.error.notFound", "API \u5730\u5740\u6216\u6A21\u578B\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u6A21\u578B\u540D\u79F0\u3002"],
              NOT_FOUND: ["ai.settings.error.notFound", "API \u5730\u5740\u6216\u6A21\u578B\u63A5\u53E3\u4E0D\u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u6A21\u578B\u540D\u79F0\u3002"],
              AI_RATE_LIMITED: ["ai.settings.error.rateLimit", "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
              RATE_LIMIT: ["ai.settings.error.rateLimit", "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
              AI_SERVER_ERROR: ["ai.settings.error.server", "\u6E20\u9053\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
              SERVER: ["ai.settings.error.server", "\u6E20\u9053\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
              AI_TIMEOUT: ["ai.settings.error.timeout", "\u8FDE\u63A5\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u6216\u7F51\u7EDC\u3002"],
              TIMEOUT: ["ai.settings.error.timeout", "\u8FDE\u63A5\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u6216\u7F51\u7EDC\u3002"],
              AI_NETWORK_ERROR: ["ai.settings.error.network", "\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u7F51\u7EDC\u3002"],
              NETWORK: ["ai.settings.error.network", "\u7F51\u7EDC\u8FDE\u63A5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u548C\u7F51\u7EDC\u3002"],
              INVALID_AI_BASE_URL: ["ai.settings.error.baseUrl", "API \u5730\u5740\u5FC5\u987B\u662F\u6709\u6548\u7684 HTTPS \u5730\u5740\u3002"],
              INVALID_SETTINGS: ["ai.settings.error.baseUrl", "API \u5730\u5740\u5FC5\u987B\u662F\u6709\u6548\u7684 HTTPS \u5730\u5740\u3002"],
              AI_INVALID_SETTINGS: ["ai.settings.error.invalidSettings", "AI \u6E20\u9053\u8BBE\u7F6E\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u5730\u5740\u3001\u6A21\u578B\u548C\u5BC6\u94A5\u3002"],
              AI_INVALID_RESPONSE: ["ai.settings.error.invalidResponse", "\u6E20\u9053\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BFB\u53D6\u6216\u4E0D\u7B26\u5408\u63A5\u53E3\u683C\u5F0F\u7684\u54CD\u5E94\u3002"],
              AI_PROVIDER_ERROR: ["ai.settings.error.provider", "\u6E20\u9053\u62D2\u7EDD\u4E86\u8BF7\u6C42\uFF0C\u4F46\u6CA1\u6709\u8FD4\u56DE\u53EF\u8BC6\u522B\u7684\u9519\u8BEF\u7C7B\u578B\u3002"],
              AI_KEY_REQUIRED: ["ai.settings.error.keyRequired", "\u8BF7\u586B\u5199 API Key\uFF0C\u6216\u4FDD\u7559\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5\u3002"],
              AI_API_KEY_REQUIRED: ["ai.settings.error.keyRequired", "\u8BF7\u586B\u5199 API Key\uFF0C\u6216\u4FDD\u7559\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5\u3002"]
            };
            const fallback = kind === "load" ? ["ai.settings.error.load", "\u8BFB\u53D6 AI \u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002"] : kind === "save" ? ["ai.settings.error.save", "\u4FDD\u5B58 AI \u8BBE\u7F6E\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002"] : ["ai.settings.error.test", "\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u7F6E\u540E\u91CD\u8BD5\u3002"];
            const [key, message] = keys[normalized] || fallback;
            const summary = tr(key, message);
            const reason = safeUpstreamReason(upstreamReason);
            return reason ? `${summary} ${tr("ai.settings.error.upstreamReason", "\u6E20\u9053\u539F\u56E0\uFF1A")}${reason}` : summary;
          }
          function validAiBaseUrl(value) {
            try {
              const url = new URL(String(value || "").trim());
              return url.protocol === "https:" && !url.username && !url.password && !url.search && !url.hash;
            } catch (_error) {
              return false;
            }
          }
          function aiResponseOk(response) {
            return response === true || Boolean(response && response !== false && response.ok !== false && !response.error);
          }
          function aiResponseCode(response, fallback = "") {
            return String(response?.errorCode || response?.code || response?.error?.code || fallback || "");
          }
          function aiResponseReason(response) {
            return safeUpstreamReason(response?.upstreamReason);
          }
          function aiSettingsEnvelope() {
            const dialog = activeDialog();
            if (!dialog) return null;
            const baseUrl = dialog.querySelector("[data-ai-base-url]")?.value.trim() || "";
            const model = dialog.querySelector("[data-ai-model]")?.value.trim() || "";
            const apiKey = dialog.querySelector("[data-ai-api-key]")?.value.trim() || "";
            const apiKeyAction = aiApiKeyAction === "clear" ? "clear" : apiKey ? "replace" : "preserve";
            const envelope = {
              settings: {
                enabled: dialog.querySelector("[data-ai-enabled]")?.getAttribute("aria-checked") === "true",
                autoOrganize: dialog.querySelector("[data-ai-auto-organize]")?.getAttribute("aria-checked") === "true",
                provider: "openai-compatible",
                baseUrl,
                model,
                excludeSensitiveSites: true
              },
              apiKeyAction
            };
            if (apiKeyAction === "replace") envelope.apiKey = apiKey;
            return envelope;
          }
          function captureDraft() {
            const dialog = activeDialog();
            if (!dialog?.querySelector("[data-ai-settings]")) return null;
            const keyInput = dialog.querySelector("[data-ai-api-key]");
            return {
              enabled: dialog.querySelector("[data-ai-enabled]")?.getAttribute("aria-checked") === "true",
              autoOrganize: dialog.querySelector("[data-ai-auto-organize]")?.getAttribute("aria-checked") === "true",
              baseUrl: dialog.querySelector("[data-ai-base-url]")?.value || "",
              model: dialog.querySelector("[data-ai-model]")?.value || "",
              apiKey: keyInput?.value || "",
              keyVisible: keyInput?.type === "text",
              apiKeyAction: aiApiKeyAction
            };
          }
          function restoreDraft(draft) {
            const dialog = activeDialog();
            if (!draft || !dialog) return false;
            const enabled = dialog.querySelector("[data-ai-enabled]");
            const autoOrganize = dialog.querySelector("[data-ai-auto-organize]");
            const baseUrl = dialog.querySelector("[data-ai-base-url]");
            const model = dialog.querySelector("[data-ai-model]");
            const keyInput = dialog.querySelector("[data-ai-api-key]");
            const toggleKey = dialog.querySelector("[data-ai-toggle-key]");
            enabled?.setAttribute("aria-checked", String(draft.enabled));
            autoOrganize?.setAttribute("aria-checked", String(draft.autoOrganize));
            if (baseUrl) baseUrl.value = draft.baseUrl;
            if (model) model.value = draft.model;
            if (keyInput) {
              keyInput.value = draft.apiKey;
              keyInput.type = draft.keyVisible ? "text" : "password";
            }
            if (toggleKey) {
              const label = tr(draft.keyVisible ? "ai.settings.hideKey" : "ai.settings.showKey", draft.keyVisible ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u5BC6\u94A5");
              toggleKey.textContent = label;
              toggleKey.setAttribute("aria-label", label);
              toggleKey.setAttribute("title", label);
              toggleKey.setAttribute("aria-pressed", String(draft.keyVisible));
            }
            aiApiKeyAction = draft.apiKeyAction;
            syncControls();
            return true;
          }
          function syncControls() {
            const dialog = activeDialog();
            if (!dialog) return;
            const pending = aiLoading || aiSaving || aiTesting;
            const content = dialog.querySelector("[data-ai-settings]");
            if (!content) return;
            content.querySelectorAll("button,input").forEach((control) => {
              control.disabled = pending || control.matches("[data-ai-clear-key]") && (aiApiKeyAction === "clear" || !aiSettings?.hasApiKey && aiApiKeyAction !== "replace");
            });
            const loading = content.querySelector("[data-ai-loading]");
            if (loading) loading.hidden = !aiLoading;
            const error = content.querySelector("[data-ai-error]");
            if (error) {
              error.textContent = aiError;
              error.hidden = !aiError;
            }
            const status = content.querySelector("[data-ai-status]");
            if (status) {
              status.textContent = aiStatus;
              status.hidden = !aiStatus;
            }
            const saveButton = content.querySelector("[data-ai-save]");
            const testButton = content.querySelector("[data-ai-test]");
            if (saveButton) saveButton.setAttribute("aria-busy", String(aiSaving));
            if (testButton) testButton.setAttribute("aria-busy", String(aiTesting));
            const hint = content.querySelector("[data-ai-action-hint]");
            if (hint && pending) hint.textContent = aiSaving ? tr("ai.settings.saving", "\u6B63\u5728\u4FDD\u5B58\u672C\u673A\u8BBE\u7F6E\u2026") : aiTesting ? tr("ai.settings.testing", "\u6B63\u5728\u6D4B\u8BD5\u8FDE\u63A5\u2026") : tr("ai.settings.loading", "\u6B63\u5728\u8BFB\u53D6\u672C\u673A\u8BBE\u7F6E\u2026");
          }
          function render() {
            const dialog = activeDialog();
            if (!dialog) return;
            const content = dialog.querySelector("#settingsContent");
            if (!content || typeof aiSettingsMarkup !== "function") return;
            const current = aiSettings || normalizeAiSettingsForView();
            content.innerHTML = aiSettingsMarkup({ ...current, loading: aiLoading }, tr);
            bindEvents();
            syncControls();
          }
          async function load() {
            const requestId = ++aiLoadRequest;
            const epoch = typeof getLifecycle === "function" ? getLifecycle() : 0;
            aiLoading = true;
            aiError = "";
            aiStatus = "";
            aiApiKeyAction = "preserve";
            render();
            let response;
            try {
              response = typeof loadAiSettings === "function" ? await loadAiSettings() : false;
            } catch (_error) {
              response = false;
            }
            if (stale(epoch) || requestId !== aiLoadRequest) return false;
            aiLoading = false;
            if (!aiResponseOk(response)) {
              aiError = aiFailure("load", aiResponseCode(response), aiResponseReason(response));
              render();
              return false;
            }
            aiSettings = normalizeAiSettingsForView(response.settings || response);
            render();
            return true;
          }
          async function requestPermission(baseUrl) {
            if (typeof requestAiOriginPermission !== "function") return false;
            try {
              const response = await requestAiOriginPermission(baseUrl);
              return response === true || response?.granted === true || response?.ok === true;
            } catch (_error) {
              return false;
            }
          }
          async function save() {
            if (aiLoading || aiSaving || aiTesting || typeof saveAiSettings !== "function") return false;
            const envelope = aiSettingsEnvelope();
            if (!envelope || !validAiBaseUrl(envelope.settings.baseUrl)) {
              aiError = aiFailure("save", "INVALID_AI_BASE_URL");
              aiStatus = "";
              syncControls();
              return false;
            }
            if (!envelope.settings.model) {
              aiError = tr("ai.settings.error.model", "\u8BF7\u586B\u5199\u6A21\u578B\u540D\u79F0\u3002");
              aiStatus = "";
              syncControls();
              return false;
            }
            aiSaving = true;
            aiError = "";
            aiStatus = "";
            syncControls();
            const epoch = typeof getLifecycle === "function" ? getLifecycle() : 0;
            const granted = await requestPermission(envelope.settings.baseUrl);
            let response;
            try {
              response = await saveAiSettings(envelope);
            } catch (_error) {
              response = false;
            }
            if (stale(epoch)) return false;
            aiSaving = false;
            if (!aiResponseOk(response)) {
              aiError = aiFailure("save", aiResponseCode(response), aiResponseReason(response));
              syncControls();
              return false;
            }
            const saved = normalizeAiSettingsForView(response.settings || envelope.settings);
            saved.hasApiKey = envelope.apiKeyAction === "replace" ? true : envelope.apiKeyAction === "clear" ? false : (response.settings?.hasApiKey ?? response.hasApiKey ?? aiSettings?.hasApiKey) === true;
            aiSettings = saved;
            aiApiKeyAction = "preserve";
            aiStatus = granted ? tr("ai.settings.saved", "AI \u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u5728\u672C\u673A\u3002") : tr("ai.settings.savedPermissionPending", "\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u8BE5\u5730\u5740\u6743\u9650\u5C1A\u672A\u6388\u6743\uFF0C\u8FDE\u63A5\u6D4B\u8BD5\u524D\u53EF\u518D\u6B21\u5141\u8BB8\u3002");
            render();
            return true;
          }
          async function testConnection() {
            if (aiLoading || aiSaving || aiTesting || typeof testAiConnection !== "function") return false;
            const envelope = aiSettingsEnvelope();
            if (!envelope || !validAiBaseUrl(envelope.settings.baseUrl)) {
              aiError = aiFailure("test", "INVALID_AI_BASE_URL");
              aiStatus = "";
              syncControls();
              return false;
            }
            if (!envelope.settings.model) {
              aiError = tr("ai.settings.error.model", "\u8BF7\u586B\u5199\u6A21\u578B\u540D\u79F0\u3002");
              aiStatus = "";
              syncControls();
              return false;
            }
            if (envelope.apiKeyAction === "clear" || envelope.apiKeyAction !== "replace" && aiSettings?.hasApiKey !== true) {
              aiError = aiFailure("test", "AI_KEY_REQUIRED");
              aiStatus = "";
              syncControls();
              return false;
            }
            aiTesting = true;
            aiError = "";
            aiStatus = "";
            syncControls();
            const epoch = typeof getLifecycle === "function" ? getLifecycle() : 0;
            if (!await requestPermission(envelope.settings.baseUrl)) {
              if (stale(epoch)) return false;
              aiTesting = false;
              aiError = aiFailure("test", "AI_PERMISSION_DENIED");
              syncControls();
              return false;
            }
            let response;
            try {
              response = await testAiConnection(envelope);
            } catch (_error) {
              response = false;
            }
            if (stale(epoch)) return false;
            aiTesting = false;
            if (!aiResponseOk(response)) {
              aiError = aiFailure("test", aiResponseCode(response), aiResponseReason(response));
              syncControls();
              return false;
            }
            aiStatus = tr("ai.settings.testSuccess", "\u8FDE\u63A5\u6210\u529F\uFF0C\u53EF\u4EE5\u4F7F\u7528\u5F53\u524D\u6E20\u9053\u3002");
            syncControls();
            return true;
          }
          function bindEvents() {
            const dialog = activeDialog();
            if (!dialog) return;
            const content = dialog.querySelector("[data-ai-settings]");
            if (!content) return;
            content.querySelectorAll("[data-ai-enabled],[data-ai-auto-organize]").forEach((button) => button.addEventListener("click", () => {
              button.setAttribute("aria-checked", String(button.getAttribute("aria-checked") !== "true"));
              aiError = "";
              aiStatus = "";
              syncControls();
            }));
            const keyInput = content.querySelector("[data-ai-api-key]");
            const toggleKey = content.querySelector("[data-ai-toggle-key]");
            const clearKey = content.querySelector("[data-ai-clear-key]");
            toggleKey?.addEventListener("click", () => {
              const showing = keyInput.type === "text";
              keyInput.type = showing ? "password" : "text";
              const label = tr(showing ? "ai.settings.showKey" : "ai.settings.hideKey", showing ? "\u663E\u793A\u5BC6\u94A5" : "\u9690\u85CF\u5BC6\u94A5");
              toggleKey.textContent = label;
              toggleKey.setAttribute("aria-label", label);
              toggleKey.setAttribute("title", label);
              toggleKey.setAttribute("aria-pressed", String(!showing));
              keyInput.focus();
            });
            clearKey?.addEventListener("click", () => {
              keyInput.value = "";
              aiApiKeyAction = "clear";
              aiError = "";
              aiStatus = tr("ai.settings.keyWillClear", "\u4FDD\u5B58\u540E\u5C06\u6E05\u9664\u672C\u673A\u5BC6\u94A5\u3002");
              clearKey.disabled = true;
              syncControls();
              keyInput.focus();
            });
            keyInput?.addEventListener("input", () => {
              aiApiKeyAction = keyInput.value.trim() ? "replace" : "preserve";
              aiError = "";
              aiStatus = "";
              syncControls();
            });
            content.querySelectorAll("[data-ai-base-url],[data-ai-model]").forEach((input) => input.addEventListener("input", () => {
              aiError = "";
              aiStatus = "";
              syncControls();
            }));
            content.querySelector("[data-ai-save]")?.addEventListener("click", save);
            content.querySelector("[data-ai-test]")?.addEventListener("click", testConnection);
            content.addEventListener("keydown", (event) => {
              if (event.key !== "Enter" || !event.target.matches("[data-ai-base-url],[data-ai-model],[data-ai-api-key]")) return;
              event.preventDefault();
              save();
            });
          }
          function enter({ reload = false, preserveDraft = false } = {}) {
            const draft = preserveDraft ? captureDraft() : null;
            if (reload) {
              aiSaving = false;
              aiTesting = false;
              aiApiKeyAction = "preserve";
            }
            if (!aiSettings || reload) load();
            else {
              render();
              restoreDraft(draft);
            }
          }
          function reset() {
            aiLoadRequest += 1;
            aiLoading = false;
            aiSaving = false;
            aiTesting = false;
            aiApiKeyAction = "preserve";
          }
          function focusTarget() {
            const dialog = activeDialog();
            return dialog?.querySelector("[data-ai-enabled]:not([disabled])") || dialog?.querySelector("#settingsDialogClose") || null;
          }
          return Object.freeze({ enter, render, reset, focusTarget });
        }
        return Object.freeze({ createDockSettingsAiSection });
      });
    }
  });

  // src/content/dock-settings-backup-section.js
  var require_dock_settings_backup_section = __commonJS({
    "src/content/dock-settings-backup-section.js"(exports, module) {
      (function attachDockSettingsBackupSection(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsBackupSection = api;
        else if (!commonJs) root.TfaDockSettingsBackupSection = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function createDockSettingsBackupSectionModule() {
        "use strict";
        const MAX_BACKUP_BYTES = 5 * 1024 * 1024;
        const DEFAULT_PENDING_TIMEOUT_MS = 30 * 1e3;
        const PENDING_TIMEOUT = Object.freeze({ timedOut: true });
        const SUMMARY_FIELDS = Object.freeze([
          "tasks",
          "pages",
          "relations",
          "categories",
          "sites",
          "dockSites"
        ]);
        const ERROR_COPY = Object.freeze({
          BACKUP_TOO_LARGE: ["privacy.backup.error.tooLarge", "\u5907\u4EFD\u6587\u4EF6\u4E0D\u80FD\u8D85\u8FC7 5 MiB\u3002"],
          BACKUP_INVALID_JSON: ["privacy.backup.error.invalidJson", "\u6240\u9009\u6587\u4EF6\u4E0D\u662F\u6709\u6548\u7684 JSON \u5907\u4EFD\u3002"],
          BACKUP_INVALID_FORMAT: ["privacy.backup.error.invalidFormat", "\u6240\u9009\u6587\u4EF6\u4E0D\u662F\u53D7\u652F\u6301\u7684 TabFlow \u5907\u4EFD\u3002"],
          BACKUP_UNSUPPORTED_VERSION: ["privacy.backup.error.unsupportedVersion", "\u8BE5\u5907\u4EFD\u7248\u672C\u6682\u4E0D\u53D7\u652F\u6301\uFF0C\u8BF7\u5347\u7EA7 TabFlow \u540E\u91CD\u8BD5\u3002"],
          BACKUP_SENSITIVE_DATA: ["privacy.backup.error.sensitiveData", "\u4E3A\u4FDD\u62A4\u9690\u79C1\uFF0C\u8BE5\u6587\u4EF6\u5305\u542B\u4E0D\u5141\u8BB8\u6062\u590D\u7684\u654F\u611F\u6570\u636E\u3002"],
          BACKUP_UNKNOWN_FIELD: ["privacy.backup.error.unknownField", "\u5907\u4EFD\u5305\u542B\u5F53\u524D\u7248\u672C\u4E0D\u652F\u6301\u7684\u6570\u636E\u5B57\u6BB5\u3002"],
          BACKUP_INVALID_RECORD: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_FIELD: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_SECTION: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_TASK: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_PAGE: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_RELATION: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_URL: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_CATEGORY: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_SITE: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_INVALID_DOCK: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          BACKUP_DUPLICATE_ID: ["privacy.backup.error.duplicate", "\u5907\u4EFD\u4E2D\u5B58\u5728\u91CD\u590D\u8BB0\u5F55\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002"],
          BACKUP_BROKEN_REFERENCE: ["privacy.backup.error.brokenReference", "\u5907\u4EFD\u4E2D\u7684\u4EFB\u52A1\u5173\u7CFB\u5F15\u7528\u7F3A\u5931\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002"],
          BACKUP_PRIMARY_CONFLICT: ["privacy.backup.error.primaryConflict", "\u5907\u4EFD\u4E2D\u540C\u4E00\u9875\u9762\u5B58\u5728\u591A\u4E2A\u4E3B\u8981\u4EFB\u52A1\uFF0C\u65E0\u6CD5\u5B89\u5168\u6062\u590D\u3002"],
          BACKUP_SECTION_UNAVAILABLE: ["privacy.backup.error.sectionUnavailable", "\u5F53\u524D\u7248\u672C\u65E0\u6CD5\u6062\u590D\u8BE5\u6587\u4EF6\u5305\u542B\u7684\u90E8\u5206\u6570\u636E\uFF1B\u5C1A\u672A\u8FDB\u884C\u4EFB\u4F55\u66F4\u6539\u3002"],
          V2_IMPORT_CONFIRMATION_REQUIRED: ["privacy.backup.error.confirmationRequired", "\u8BF7\u5148\u660E\u786E\u786E\u8BA4\u8986\u76D6\u672C\u5730\u6570\u636E\u3002"],
          BACKUP_IMPORT_ROLLED_BACK: ["privacy.backup.error.rolledBack", "\u6062\u590D\u5931\u8D25\uFF0C\u539F\u6709\u6570\u636E\u5DF2\u6062\u590D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
          BACKUP_IMPORT_PARTIAL_ROLLBACK: ["privacy.backup.error.partialRollback", "\u6062\u590D\u5931\u8D25\uFF0C\u90E8\u5206\u539F\u6709\u6570\u636E\u53EF\u80FD\u672A\u80FD\u8FD8\u539F\u3002\u8BF7\u505C\u6B62\u7F16\u8F91\u3001\u4FDD\u7559\u5907\u4EFD\u5E76\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002"],
          BACKUP_IMPORT_IN_PROGRESS: ["privacy.backup.error.inProgress", "\u53E6\u4E00\u9879\u6062\u590D\u6B63\u5728\u8FDB\u884C\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210\u540E\u91CD\u8BD5\u3002"],
          V2_IMPORT_PREVIEW_REQUIRED: ["privacy.backup.error.previewRequired", "\u6062\u590D\u9884\u89C8\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u5907\u4EFD\u6587\u4EF6\u5E76\u518D\u6B21\u786E\u8BA4\u3002"],
          V2_IMPORT_RECONCILE_FAILED: ["privacy.backup.error.reconcileFailed", "\u6570\u636E\u5DF2\u5199\u5165\uFF0C\u4F46\u5F53\u524D\u9875\u7B7E\u72B6\u6001\u540C\u6B65\u5931\u8D25\u3002\u8BF7\u91CD\u65B0\u52A0\u8F7D\u6269\u5C55\u540E\u6838\u5BF9\u3002"],
          V2_IMPORT_TIMEOUT: ["privacy.backup.error.timeout", "\u5907\u4EFD\u64CD\u4F5C\u7B49\u5F85\u8D85\u65F6\uFF0C\u5DF2\u6E05\u9664\u6240\u9009\u6587\u4EF6\u3002\u8BF7\u91CD\u65B0\u9009\u62E9\u540E\u91CD\u8BD5\u3002"],
          V2_IMPORT_UNAVAILABLE: ["privacy.backup.error.unavailable", "\u5907\u4EFD\u6062\u590D\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
          V2_IMPORT_FAILED: ["privacy.backup.error.failed", "\u6062\u590D\u5907\u4EFD\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"],
          V2_IMPORT_INVALID: ["privacy.backup.error.invalidContent", "\u5907\u4EFD\u5185\u5BB9\u635F\u574F\u6216\u4E0D\u7B26\u5408\u5F53\u524D\u683C\u5F0F\u3002"],
          FILE_READ_FAILED: ["privacy.backup.error.readFailed", "\u65E0\u6CD5\u8BFB\u53D6\u6240\u9009\u6587\u4EF6\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u3002"]
        });
        function safeCode(value) {
          const code = String(value || "").trim().toUpperCase();
          return /^[A-Z][A-Z0-9_]{0,79}$/.test(code) ? code : "";
        }
        function responseCode(response, fallback = "") {
          return safeCode(response?.code || response?.errorCode || response?.error?.code || fallback);
        }
        function safePreviewToken(value) {
          const token = String(value || "");
          return /^[A-Za-z0-9._:-]{8,160}$/.test(token) ? token : "";
        }
        function translate(tr, key, fallback, params = {}) {
          const translated = typeof tr === "function" ? tr(key, fallback, params) : "";
          const base = translated && translated !== key ? translated : fallback;
          return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
        }
        function backupErrorMessage(code, tr) {
          const [key, fallback] = ERROR_COPY[safeCode(code)] || ["privacy.backup.error.failed", "\u6062\u590D\u5907\u4EFD\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"];
          return translate(tr, key, fallback);
        }
        function integer(value) {
          const parsed = Number(value);
          return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
        }
        function normalizeBackupPreview(response) {
          if (!response || response.ok !== true) return null;
          const summary = Object.fromEntries(SUMMARY_FIELDS.map((field) => [field, integer(response.summary?.[field])]));
          return Object.freeze({
            formatVersion: integer(response.formatVersion),
            migratedFrom: integer(response.migratedFrom) || null,
            summary: Object.freeze(summary),
            sections: Object.freeze({
              taskGraph: response.sections?.taskGraph === true,
              siteSpace: response.sections?.siteSpace === true,
              dock: response.sections?.dock === true
            })
          });
        }
        function byteLength(value) {
          const text = String(value || "");
          if (typeof TextEncoder === "function") return new TextEncoder().encode(text).byteLength;
          if (typeof Buffer === "function") return Buffer.byteLength(text, "utf8");
          return unescape(encodeURIComponent(text)).length;
        }
        function escapeMarkup(value) {
          return String(value ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
        }
        async function defaultReadFileText(file) {
          if (file && typeof file.text === "function") return file.text();
          const Reader = globalThis.FileReader;
          if (typeof Reader !== "function") throw new Error("FileReader is unavailable");
          return new Promise((resolve, reject) => {
            const reader = new Reader();
            reader.addEventListener("load", () => resolve(String(reader.result || "")));
            reader.addEventListener("error", () => reject(new Error("File read failed")));
            reader.readAsText(file);
          });
        }
        function safeBackupFilename(backup, timestamp = Date.now()) {
          const rawVersion = String(backup?.extensionVersion || `format-${Number(backup?.formatVersion) || 2}`);
          const safeVersion = rawVersion.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "").slice(0, 48) || "unknown";
          const parsedDate = new Date(timestamp);
          const localDate = Number.isFinite(parsedDate.getTime()) ? parsedDate : /* @__PURE__ */ new Date();
          const date = [
            localDate.getFullYear(),
            String(localDate.getMonth() + 1).padStart(2, "0"),
            String(localDate.getDate()).padStart(2, "0")
          ].join("-");
          return `tabflow-v2-safe-backup-${safeVersion}-${date}.json`;
        }
        function createDockSettingsBackupSection(deps = {}) {
          const {
            getContainer,
            tr,
            previewV2Import,
            importV2Data,
            readFileText = defaultReadFileText,
            onPendingChange,
            onImported,
            onStateChange,
            maxBytes = MAX_BACKUP_BYTES,
            pendingTimeoutMs = DEFAULT_PENDING_TIMEOUT_MS,
            setTimeoutFn = setTimeout,
            clearTimeoutFn = clearTimeout
          } = deps;
          let phase = "idle";
          let preview = null;
          let backupText = null;
          let previewToken = "";
          let errorCode = "";
          let statusKey = "";
          let requestSequence = 0;
          let destroyed = false;
          let pendingNotified = false;
          let externalPending = false;
          function container() {
            return typeof getContainer === "function" ? getContainer() : null;
          }
          function pending() {
            return phase === "reading" || phase === "previewing" || phase === "importing";
          }
          function locked() {
            return pending() || externalPending;
          }
          async function waitForPending(work, { timeoutEnabled = true } = {}) {
            const settled = Promise.resolve().then(work).then(
              (value) => ({ value }),
              (error) => ({ error })
            );
            if (!timeoutEnabled) return settled;
            const timeoutMs = Number.isFinite(Number(pendingTimeoutMs)) && Number(pendingTimeoutMs) > 0 ? Number(pendingTimeoutMs) : DEFAULT_PENDING_TIMEOUT_MS;
            let timer = null;
            const timeout = new Promise((resolve) => {
              timer = setTimeoutFn(() => resolve(PENDING_TIMEOUT), timeoutMs);
            });
            try {
              return await Promise.race([settled, timeout]);
            } finally {
              if (timer !== null) clearTimeoutFn(timer);
            }
          }
          function notify() {
            const isPending = pending();
            if (isPending !== pendingNotified) {
              pendingNotified = isPending;
              if (typeof onPendingChange === "function") onPendingChange(isPending);
            }
            if (typeof onStateChange === "function") onStateChange(inspect());
          }
          function countMarkup(field, key, fallback) {
            return `<li><span>${escapeMarkup(translate(tr, key, fallback))}</span><strong>${preview.summary[field]}</strong></li>`;
          }
          function previewMarkup() {
            if (!preview) return "";
            const legacy = preview.migratedFrom === 1 ? `<div class="dock-site-empty" data-backup-legacy>${escapeMarkup(translate(
              tr,
              "privacy.backup.legacy",
              "\u65E7\u7248\u5907\u4EFD\uFF1A\u53EA\u4F1A\u8986\u76D6\u4EFB\u52A1\u3001\u9875\u9762\u548C\u4EFB\u52A1\u5173\u7CFB\uFF0C\u4E0D\u4F1A\u8986\u76D6\u65B0\u9875\u5E38\u7528\u7F51\u7AD9\u6216\u7F51\u9875\u62D3\u5C55\u575E\u3002"
            ))}</div>` : "";
            const sectionLabels = [
              preview.sections.taskGraph && translate(tr, "privacy.backup.section.tasks", "\u4EFB\u52A1\u6570\u636E"),
              preview.sections.siteSpace && translate(tr, "privacy.backup.section.newtab", "\u65B0\u9875\u5E38\u7528\u7F51\u7AD9"),
              preview.sections.dock && translate(tr, "privacy.backup.section.dock", "\u7F51\u9875\u62D3\u5C55\u575E")
            ].filter(Boolean).map(escapeMarkup).join("\u3001");
            return `<div class="dock-site-empty" data-backup-preview>
        <h4>${escapeMarkup(translate(tr, "privacy.backup.previewTitle", "\u6062\u590D\u5185\u5BB9\u9884\u89C8"))}</h4>
        ${legacy}
        <div data-backup-sections>${escapeMarkup(translate(
              tr,
              "privacy.backup.willOverwrite",
              "\u5C06\u8986\u76D6\uFF1A{sections}",
              { sections: sectionLabels || translate(tr, "privacy.backup.section.none", "\u65E0\u53EF\u6062\u590D\u6570\u636E") }
            ))}</div>
        <ul class="backup-summary-list">
          ${countMarkup("tasks", "privacy.backup.count.tasks", "\u4EFB\u52A1")}
          ${countMarkup("pages", "privacy.backup.count.pages", "\u9875\u9762")}
          ${countMarkup("relations", "privacy.backup.count.relations", "\u4EFB\u52A1\u5173\u7CFB")}
          ${countMarkup("categories", "privacy.backup.count.categories", "\u65B0\u9875\u5206\u7C7B")}
          ${countMarkup("sites", "privacy.backup.count.sites", "\u5E38\u7528\u7F51\u7AD9")}
          ${countMarkup("dockSites", "privacy.backup.count.dockSites", "\u62D3\u5C55\u575E\u7F51\u7AD9")}
        </ul>
        <p class="deactivation-error" data-backup-warning>${escapeMarkup(translate(
              tr,
              "privacy.backup.overwriteWarning",
              "\u6062\u590D\u4F1A\u8986\u76D6\u4E0A\u8FF0\u672C\u5730\u6570\u636E\uFF1B\u4E0D\u4F1A\u6062\u590D API \u5BC6\u94A5\u3001\u6FC0\u6D3B\u4FE1\u606F\u3001\u6D4F\u89C8\u8BB0\u5F55\u6216\u5F53\u524D\u771F\u5B9E\u9875\u7B7E\u3002"
            ))}</p>
        <div class="deactivation-actions">
          <button class="btn" type="button" data-backup-cancel>${escapeMarkup(translate(tr, "privacy.backup.cancel", "\u53D6\u6D88"))}</button>
          <button class="btn deactivation-danger" type="button" data-backup-confirm>${escapeMarkup(translate(
              tr,
              phase === "importing" ? "privacy.backup.importing" : "privacy.backup.confirm",
              phase === "importing" ? "\u6B63\u5728\u6062\u590D\u2026" : "\u8986\u76D6\u5E76\u6062\u590D"
            ))}</button>
        </div>
      </div>`;
          }
          function markup() {
            const error = errorCode ? `<div class="deactivation-error" data-backup-error role="alert">${escapeMarkup(backupErrorMessage(errorCode, tr))}</div>` : "";
            const status = statusKey ? `<div class="dock-site-empty" data-backup-status role="status" aria-live="polite">${escapeMarkup(translate(
              tr,
              statusKey,
              statusKey === "privacy.backup.importSuccess" ? "\u6062\u590D\u5B8C\u6210\uFF0C\u4EFB\u52A1\u3001\u65B0\u9875\u548C\u7F51\u9875\u62D3\u5C55\u575E\u5DF2\u540C\u6B65\u3002" : "\u6B63\u5728\u6821\u9A8C\u5907\u4EFD\u2026"
            ))}</div>` : "";
            return `<section class="dock-site-section" data-backup-settings>
        <h3>${escapeMarkup(translate(tr, "privacy.backup.title", "\u5907\u4EFD\u4E0E\u6062\u590D"))}</h3>
        <div class="dock-site-empty">${escapeMarkup(translate(
              tr,
              "privacy.backup.hint",
              "\u9009\u62E9 TabFlow JSON \u5907\u4EFD\u540E\u4F1A\u5148\u663E\u793A\u5B89\u5168\u6458\u8981\uFF1B\u786E\u8BA4\u524D\u4E0D\u4F1A\u8986\u76D6\u4EFB\u4F55\u6570\u636E\u3002\u6587\u4EF6\u6700\u5927 5 MiB\u3002"
            ))}</div>
        <p class="deactivation-error" data-backup-reinstall-warning>${escapeMarkup(translate(
              tr,
              "privacy.backup.reinstallWarning",
              "\u5347\u7EA7\u6216\u91CD\u65B0\u52A0\u8F7D\u4F1A\u4FDD\u7559\u672C\u5730\u6570\u636E\uFF1B\u5378\u8F7D\u6269\u5C55\u901A\u5E38\u4F1A\u5220\u9664\u672C\u5730\u6570\u636E\u3002\u5378\u8F7D\u524D\u8BF7\u5148\u5BFC\u51FA\u5B89\u5168\u5907\u4EFD\uFF0C\u91CD\u88C5\u540E\u518D\u4ECE\u5907\u4EFD\u6062\u590D\u3002"
            ))}</p>
        <input type="file" accept=".json,application/json" data-backup-file hidden>
        <div class="deactivation-actions">
          <button class="btn" type="button" data-backup-choose>${escapeMarkup(translate(
              tr,
              phase === "reading" || phase === "previewing" ? "privacy.backup.reading" : "privacy.backup.choose",
              phase === "reading" || phase === "previewing" ? "\u6B63\u5728\u6821\u9A8C\u2026" : "\u4ECE\u5907\u4EFD\u6062\u590D"
            ))}</button>
        </div>
        ${status}${error}${previewMarkup()}
      </section>`;
          }
          function syncControls() {
            const host = container();
            if (!host) return;
            host.querySelectorAll?.("button,input").forEach((control) => {
              const canCancel = control.matches?.("[data-backup-cancel]") && !pending();
              control.disabled = locked() && !canCancel;
              control.setAttribute?.("aria-busy", String(pending()));
            });
          }
          function bindEvents() {
            const host = container();
            if (!host) return;
            const input = host.querySelector?.("[data-backup-file]");
            host.querySelector?.("[data-backup-choose]")?.addEventListener("click", () => input?.click?.());
            input?.addEventListener("change", () => {
              const file = input.files?.[0];
              if (file) void selectFile(file);
              input.value = "";
            });
            host.querySelector?.("[data-backup-cancel]")?.addEventListener("click", cancel);
            host.querySelector?.("[data-backup-confirm]")?.addEventListener("click", () => {
              void confirmImport();
            });
            syncControls();
          }
          function render() {
            const host = container();
            if (!host) {
              notify();
              return false;
            }
            host.innerHTML = markup();
            bindEvents();
            notify();
            return true;
          }
          function fail(code) {
            phase = "error";
            preview = null;
            previewToken = "";
            errorCode = safeCode(code) || "V2_IMPORT_FAILED";
            statusKey = "";
            render();
            return false;
          }
          async function selectFile(file) {
            if (destroyed || locked() || !file) return false;
            const declaredSize = Number(file.size);
            if (Number.isFinite(declaredSize) && declaredSize > maxBytes) {
              backupText = null;
              return fail("BACKUP_TOO_LARGE");
            }
            const sequence = ++requestSequence;
            phase = "reading";
            preview = null;
            backupText = null;
            errorCode = "";
            statusKey = "";
            previewToken = "";
            render();
            const readResult = await waitForPending(() => readFileText(file));
            if (destroyed || sequence !== requestSequence) return false;
            if (readResult === PENDING_TIMEOUT) {
              backupText = null;
              return fail("V2_IMPORT_TIMEOUT");
            }
            if (readResult.error) return fail("FILE_READ_FAILED");
            const text = readResult.value;
            if (byteLength(text) > maxBytes) return fail("BACKUP_TOO_LARGE");
            backupText = String(text);
            phase = "previewing";
            render();
            const previewResult = await waitForPending(() => typeof previewV2Import === "function" ? previewV2Import(backupText) : null);
            if (destroyed || sequence !== requestSequence) return false;
            if (previewResult === PENDING_TIMEOUT) {
              backupText = null;
              return fail("V2_IMPORT_TIMEOUT");
            }
            const response = previewResult.error ? null : previewResult.value;
            const nextPreview = normalizeBackupPreview(response);
            const nextPreviewToken = safePreviewToken(response?.previewToken);
            if (!nextPreview || !nextPreviewToken) {
              backupText = null;
              return fail(responseCode(response, "V2_IMPORT_INVALID"));
            }
            preview = nextPreview;
            previewToken = nextPreviewToken;
            phase = "preview";
            errorCode = "";
            render();
            container()?.querySelector?.("[data-backup-confirm]")?.focus?.({ preventScroll: true });
            return true;
          }
          async function confirmImport() {
            if (destroyed || locked() || phase !== "preview" || !preview || backupText === null || !previewToken || typeof importV2Data !== "function") return false;
            const sequence = ++requestSequence;
            phase = "importing";
            errorCode = "";
            statusKey = "";
            render();
            const importResult = await waitForPending(
              () => importV2Data(backupText, previewToken),
              { timeoutEnabled: false }
            );
            if (destroyed || sequence !== requestSequence) return false;
            const response = importResult.error ? null : importResult.value;
            if (response?.ok !== true) {
              const code = responseCode(response, "V2_IMPORT_FAILED");
              if (code === "BACKUP_IMPORT_PARTIAL_ROLLBACK" || code === "V2_IMPORT_PREVIEW_REQUIRED" || response?.committed === true || response?.maintenanceDegraded === true) {
                backupText = null;
                previewToken = "";
                preview = null;
              }
              phase = backupText === null ? "error" : "preview";
              errorCode = code || "V2_IMPORT_FAILED";
              render();
              return false;
            }
            const safeResult = Object.freeze({
              formatVersion: preview.formatVersion,
              migratedFrom: preview.migratedFrom,
              summary: preview.summary,
              sections: preview.sections
            });
            backupText = null;
            previewToken = "";
            preview = null;
            phase = "success";
            errorCode = "";
            statusKey = "privacy.backup.importSuccess";
            render();
            if (typeof onImported === "function") {
              try {
                onImported(safeResult);
              } catch (_error) {
              }
            }
            return true;
          }
          function cancel() {
            if (destroyed || pending()) return false;
            requestSequence += 1;
            backupText = null;
            previewToken = "";
            preview = null;
            phase = "idle";
            errorCode = "";
            statusKey = "";
            render();
            container()?.querySelector?.("[data-backup-choose]")?.focus?.({ preventScroll: true });
            return true;
          }
          function reset({ render: shouldRender = false } = {}) {
            requestSequence += 1;
            backupText = null;
            previewToken = "";
            preview = null;
            phase = "idle";
            errorCode = "";
            statusKey = "";
            if (shouldRender) render();
            else notify();
            return true;
          }
          function setExternalPending(value) {
            externalPending = value === true;
            syncControls();
            notify();
            return externalPending;
          }
          function destroy() {
            if (destroyed) return false;
            reset();
            destroyed = true;
            notify();
            return true;
          }
          function inspect() {
            return Object.freeze({
              phase,
              pending: pending(),
              externalPending,
              hasPreview: Boolean(preview),
              hasBufferedFile: backupText !== null,
              errorCode,
              preview
            });
          }
          return Object.freeze({
            render,
            markup,
            selectFile,
            confirmImport,
            cancel,
            reset,
            setExternalPending,
            destroy,
            inspect
          });
        }
        return Object.freeze({
          MAX_BACKUP_BYTES,
          DEFAULT_PENDING_TIMEOUT_MS,
          ERROR_COPY,
          backupErrorMessage,
          normalizeBackupPreview,
          safeBackupFilename,
          createDockSettingsBackupSection
        });
      });
    }
  });

  // src/content/dock-settings-view.js
  var require_dock_settings_view = __commonJS({
    "src/content/dock-settings-view.js"(exports, module) {
      (function(root, factory) {
        const aiSectionModule = typeof module === "object" && module.exports ? require_dock_settings_ai_section() : root.TfaDockSettingsAiSection;
        const backupSectionModule = typeof module === "object" && module.exports ? require_dock_settings_backup_section() : root.TfaDockSettingsBackupSection;
        const api = factory(aiSectionModule, backupSectionModule), commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSettingsView = api;
        else if (!commonJs) root.TfaDockSettingsView = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function(aiSectionModule, backupSectionModule) {
        "use strict";
        function createDockSettingsView(deps) {
          const {
            document: document2,
            context,
            settingsRuntime,
            activationRuntime,
            escapeHtml,
            normalizeDockUrl,
            normalizeDockColor,
            MOTION_LIMITS,
            APPEARANCE_LIMITS,
            DOCK_PREVIEW_FALLBACK_SITES,
            moveDockSiteUrls,
            reorderDockSitesByUrls,
            dockSettingsFaviconMarkup,
            bindDockSettingsFaviconFallbacks,
            bindDockFaviconElements,
            dockItemMarkup,
            stopDockPreviewAnimation,
            initializeDockPreviewAnimation,
            renderDockMotionPreview,
            resetDockMotionSettings,
            previewRuntime,
            persistWebDockSite,
            removeDockSite,
            persistDockSiteOrder,
            setOptimisticConfig,
            renderWebDock,
            applyWebDockConfig,
            dockResponseMessage,
            syncHostPointerInteractivity,
            setWebDockExpanded,
            renderActivationPanel,
            refreshLicenseState,
            closePurchasePreview,
            closeLicenseDeactivationDialog,
            ensureHost,
            queueDockVisualPersistence,
            flushDockVisualPersistence,
            clearBehaviorData,
            clearAiData,
            clearAllData,
            exportV2Data,
            previewV2Import,
            importV2Data,
            confirmBehaviorClear,
            requestFrame = requestAnimationFrame,
            loadAiSettings,
            saveAiSettings,
            testAiConnection,
            requestAiOriginPermission,
            loadLanguagePreference,
            saveLanguagePreference,
            cancelFrame = typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : () => {
            },
            setTimeoutFn = setTimeout,
            clearTimeoutFn = clearTimeout
          } = deps;
          const { dockSiteRowMarkup, motionStep, formatDockAppearanceOutput, applyDockTileAppearanceVariables, rowIdentity, findRow, aiSettingsMarkup } = deps.markup;
          const translate = typeof deps.t === "function" ? deps.t : typeof deps.translate === "function" ? deps.translate : () => "";
          function tr(key, fallback, params = {}) {
            const translated = translate(key, params), base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] == null ? "" : params[name]));
          }
          let settingsDialog = null, settingsSection = "activation", settingsDialogRestoreFocus = null, dockSiteDraft = { title: "", url: "" };
          let dockSiteError = "", dockSettingsError = "", behaviorClearStatus = "", behaviorClearError = "", behaviorClearPending = false, backupPending = false;
          let destroyed = false, openFrame = null, focusTimer = null, lifecycle = 0;
          function cancelOpenEffects() {
            lifecycle += 1;
            if (openFrame !== null) cancelFrame(openFrame);
            if (focusTimer !== null) clearTimeoutFn(focusTimer);
            openFrame = null;
            focusTimer = null;
          }
          function restoreRowStates() {
            if (!settingsDialog) return;
            const rows = Array.from(settingsDialog.querySelectorAll("[data-dock-site-id]"));
            settingsRuntime.getDirtyRowDrafts().forEach((state) => {
              const row = rows.find((candidate) => candidate.dataset.dockSiteIndex === state.index && candidate.dataset.dockSiteUrl === state.originalUrl) || rows.find((candidate) => candidate.dataset.dockSiteIndex === state.index && candidate.dataset.dockSiteId === state.siteId);
              if (!row || !state.dirty) return;
              const title = row.querySelector("[data-dock-site-title]"), url = row.querySelector("[data-dock-site-url]");
              if (title) title.value = state.title;
              if (url) url.value = state.url;
              row.dataset.dockSiteStateUrl = state.originalUrl;
              row.dataset.dockSiteDirty = "true";
            });
          }
          const createAiSection = deps.createDockSettingsAiSection || aiSectionModule?.createDockSettingsAiSection;
          if (typeof createAiSection !== "function") throw new TypeError("Dock settings view requires its AI section");
          const aiSection = createAiSection({
            getDialog: () => settingsDialog,
            isActive: () => settingsSection === "ai",
            getLifecycle: () => lifecycle,
            isDestroyed: () => destroyed,
            aiSettingsMarkup,
            tr,
            loadAiSettings,
            saveAiSettings,
            testAiConnection,
            requestAiOriginPermission
          });
          const createBackupSection = deps.createDockSettingsBackupSection || backupSectionModule?.createDockSettingsBackupSection;
          if (typeof createBackupSection !== "function") throw new TypeError("Dock settings view requires its backup section");
          const backupSection = createBackupSection({
            getContainer: () => settingsDialog?.querySelector?.("[data-backup-section-host]") || null,
            tr,
            previewV2Import,
            importV2Data,
            setTimeoutFn,
            clearTimeoutFn,
            onPendingChange(value) {
              backupPending = value === true;
              syncPrivacyDataControls();
            }
          });
          const pageWindow = document2?.defaultView || null;
          function handlePagehide() {
            forceClose(false);
          }
          pageWindow?.addEventListener?.("pagehide", handlePagehide);
          function selectSettingsSection(section) {
            if (!settingsDialog) return;
            const nextSection = ["web-dock", "privacy-data", "ai"].includes(section) ? section : "activation";
            if (backupPending && settingsSection === "privacy-data" && nextSection !== "privacy-data") return;
            const enteringWebDock = settingsSection !== "web-dock" && nextSection === "web-dock";
            const enteringAi = settingsSection !== "ai" && nextSection === "ai";
            if (settingsSection === "web-dock" && nextSection !== "web-dock") {
              flushDockVisualPersistence();
              stopDockPreviewAnimation();
              settingsRuntime.clearVisualDraft();
            }
            if (settingsSection === "privacy-data" && nextSection !== "privacy-data") backupSection.reset();
            settingsSection = nextSection;
            settingsDialog.querySelectorAll("[data-settings-section]").forEach((button) => {
              const active = button.dataset.settingsSection === settingsSection;
              button.classList.toggle("active", active);
              if (active) button.setAttribute("aria-current", "page");
              else button.removeAttribute("aria-current");
            });
            const title = settingsDialog.querySelector("#settingsDialogTitle"), description = settingsDialog.querySelector("#settingsDialogDescription"), content = settingsDialog.querySelector("#settingsContent");
            if (!title || !description || !content) return;
            if (settingsSection === "web-dock") {
              if (enteringWebDock || !settingsRuntime.getVisualDraft()) syncDockVisualDraftFromConfig(context().config);
              title.textContent = tr("settings.dockTitle", "\u7F51\u9875\u62D3\u5C55\u575E");
              description.textContent = tr("settings.dockDescription", "\u7BA1\u7406\u56FA\u5B9A\u7F51\u7AD9\u3001\u5916\u89C2\u4E0E\u4EA4\u4E92");
              renderWebDockSettings();
              return;
            }
            if (settingsSection === "privacy-data") {
              title.textContent = tr("privacy.title", "\u9690\u79C1\u4E0E\u6570\u636E");
              description.textContent = tr("privacy.description", "\u67E5\u770B\u672C\u5730\u884C\u4E3A\u8BB0\u5F55\u5E76\u7BA1\u7406\u4FDD\u7559\u6570\u636E");
              renderPrivacyDataSettings();
              return;
            }
            if (settingsSection === "ai") {
              title.textContent = tr("ai.settings.title", "AI \u667A\u80FD\u6574\u7406");
              description.textContent = tr("ai.settings.description", "\u914D\u7F6E\u865A\u62DF\u4EFB\u52A1\u6574\u7406\u3001\u6A21\u578B\u6E20\u9053\u548C\u672C\u673A\u5BC6\u94A5");
              aiSection.enter({ reload: enteringAi, preserveDraft: !enteringAi });
              return;
            }
            title.textContent = tr("settings.activationTitle", "\u6C38\u4E45\u6FC0\u6D3B");
            description.textContent = tr("settings.activationDescription", "\u6FC0\u6D3B\u589E\u503C\u6743\u76CA\uFF0C\u514D\u8D39\u6838\u5FC3\u529F\u80FD\u59CB\u7EC8\u53EF\u7528");
            content.innerHTML = '<div class="activation-panel" id="activationPanel"></div>';
            renderActivationPanel();
          }
          const syncDockVisualDraftFromConfig = settingsRuntime.syncVisualDraft, ensureDockVisualDraft = settingsRuntime.ensureVisualDraft, updateDockVisualDraft = settingsRuntime.updateVisualDraft;
          function syncPrivacyDataControls() {
            if (!settingsDialog || settingsSection !== "privacy-data") return;
            const button = settingsDialog.querySelector("[data-clear-behavior-history]");
            const status = settingsDialog.querySelector("[data-behavior-clear-status]"), error = settingsDialog.querySelector("[data-behavior-clear-error]");
            const anyPending = behaviorClearPending || backupPending;
            if (button) {
              button.disabled = anyPending;
              button.setAttribute("aria-busy", String(behaviorClearPending));
              button.textContent = behaviorClearPending ? tr("privacy.clearing", "\u6B63\u5728\u6E05\u9664\u2026") : tr("privacy.clearBehavior", "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9");
            }
            settingsDialog.querySelectorAll("[data-v2-data-action]").forEach((control) => {
              control.disabled = anyPending;
              control.setAttribute("aria-busy", String(behaviorClearPending));
            });
            backupSection.setExternalPending(behaviorClearPending);
            if (status) {
              status.textContent = behaviorClearStatus;
              status.hidden = !behaviorClearStatus;
            }
            if (error) {
              error.textContent = behaviorClearError;
              error.hidden = !behaviorClearError;
            }
          }
          async function runDataAction(request, confirmation, success) {
            if (behaviorClearPending || backupPending || typeof request !== "function" || confirmation && !confirmBehaviorClear?.(confirmation)) return false;
            behaviorClearPending = true;
            behaviorClearStatus = "";
            behaviorClearError = "";
            syncPrivacyDataControls();
            let response;
            try {
              response = await request();
            } catch (_error) {
              response = null;
            }
            behaviorClearPending = false;
            if (response?.ok === true) behaviorClearStatus = success;
            else behaviorClearError = String(response?.error || response?.message || tr("privacy.actionFailed", "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002"));
            syncPrivacyDataControls();
            return response?.ok === true ? response : false;
          }
          async function clearBehaviorHistory() {
            if (behaviorClearPending || backupPending || typeof confirmBehaviorClear !== "function" || !confirmBehaviorClear(tr("privacy.clearBehaviorConfirm", "\u786E\u5B9A\u6E05\u9664\u672C\u673A\u6700\u8FD1 30 \u5929\u7684\u884C\u4E3A\u8F68\u8FF9\u5417\uFF1F\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u9875\u9762\u8D44\u6599\u548C\u5F53\u524D\u9875\u7B7E\u4E0D\u4F1A\u88AB\u5220\u9664\u3002"))) return false;
            behaviorClearPending = true;
            behaviorClearStatus = "";
            behaviorClearError = "";
            syncPrivacyDataControls();
            let response;
            try {
              response = await clearBehaviorData();
            } catch (_error) {
              response = null;
            }
            behaviorClearPending = false;
            if (response?.ok === true) {
              const deleted = Number.isFinite(Number(response.deleted)) ? Math.max(0, Math.trunc(Number(response.deleted))) : 0;
              behaviorClearStatus = tr("privacy.clearBehaviorSuccess", "\u5DF2\u6E05\u9664 {count} \u6761\u884C\u4E3A\u8BB0\u5F55\uFF1B\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u548C\u5F53\u524D\u9875\u7B7E\u4FDD\u6301\u4E0D\u53D8\u3002", { count: deleted });
            } else behaviorClearError = String(response?.error || response?.message || tr("privacy.clearBehaviorFailed", "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002"));
            syncPrivacyDataControls();
            return response?.ok === true;
          }
          async function clearAiHistory() {
            return runDataAction(clearAiData, tr("privacy.clearAiConfirm", "\u786E\u5B9A\u6E05\u9664\u672C\u673A AI \u8BBE\u7F6E\u3001API \u5BC6\u94A5\u548C\u8FD0\u884C\u72B6\u6001\u5417\uFF1F\u4EFB\u52A1\u548C\u4EFB\u52A1\u5173\u7CFB\u4E0D\u4F1A\u88AB\u5220\u9664\u3002"), tr("privacy.clearAiSuccess", "\u5DF2\u6E05\u9664\u672C\u673A AI \u6570\u636E\uFF1B\u4EFB\u52A1\u548C\u4EFB\u52A1\u5173\u7CFB\u4FDD\u6301\u4E0D\u53D8\u3002"));
          }
          async function clearEverything() {
            return runDataAction(clearAllData, tr("privacy.clearAllConfirm", "\u786E\u5B9A\u6E05\u9664\u5168\u90E8 TabFlow \u672C\u5730\u6570\u636E\u5417\uFF1F\u6B64\u64CD\u4F5C\u4F1A\u5220\u9664\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u884C\u4E3A\u8BB0\u5F55\u548C AI \u5BC6\u94A5\uFF0C\u4E14\u65E0\u6CD5\u64A4\u9500\u3002"), tr("privacy.clearAllSuccess", "\u5DF2\u6E05\u9664\u5168\u90E8 TabFlow \u672C\u5730\u6570\u636E\u3002"));
          }
          async function exportSafeData() {
            const response = await runDataAction(exportV2Data, "", tr("privacy.exportSuccess", "\u5B89\u5168\u6570\u636E\u5DF2\u5BFC\u51FA\u3002"));
            if (!response?.export) return false;
            try {
              const view = document2.defaultView || globalThis, blob = new view.Blob([JSON.stringify(response.export)], { type: "application/json" });
              const url = view.URL.createObjectURL(blob), anchor = document2.createElement("a");
              anchor.href = url;
              anchor.download = backupSectionModule.safeBackupFilename(response.export);
              anchor.hidden = true;
              document2.body.appendChild(anchor);
              anchor.click();
              anchor.remove();
              view.URL.revokeObjectURL(url);
              return true;
            } catch (_error) {
              behaviorClearStatus = "";
              behaviorClearError = tr("privacy.exportFailed", "\u5BFC\u51FA\u6587\u4EF6\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
              syncPrivacyDataControls();
              return false;
            }
          }
          function renderPrivacyDataSettings() {
            if (!settingsDialog || settingsSection !== "privacy-data") return;
            const content = settingsDialog.querySelector("#settingsContent");
            if (!content) return;
            content.innerHTML = `<div class="web-dock-settings" data-privacy-data-settings><section class="dock-site-section"><h3>${tr("privacy.localBehavior", "\u672C\u5730\u884C\u4E3A\u8BB0\u5F55")}</h3><div class="dock-site-empty">${tr("privacy.localBehaviorHint", "\u53EA\u5728\u672C\u673A\u8BB0\u5F55\u6253\u5F00\u3001\u5207\u6362\u3001\u8DF3\u8F6C\u3001\u5173\u95ED\u7B49\u5FC5\u8981\u9875\u7B7E\u884C\u4E3A\uFF0C\u6700\u957F\u4FDD\u7559 30 \u5929\u3002\u6570\u636E\u4E0D\u4F1A\u4E0A\u4F20\uFF0C\u4E5F\u4E0D\u4F1A\u81EA\u52A8\u6539\u53D8\u4EFB\u52A1\u5173\u7CFB\u6216\u771F\u5B9E\u9875\u7B7E\u987A\u5E8F\u3002")}</div></section><div data-backup-section-host></div><section class="dock-site-section"><h3>${tr("privacy.dataControls", "\u6570\u636E\u7BA1\u7406")}</h3><div class="dock-site-empty">${tr("privacy.behaviorClearHint", "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9\u4E0D\u4F1A\u5220\u9664\u4EFB\u52A1\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u9875\u9762\u8D44\u6599\u6216\u5F53\u524D\u9875\u7B7E\u5B9E\u4F8B\u3002")}</div><div class="dock-site-empty">${tr("privacy.exportHint", "\u5B89\u5168\u5BFC\u51FA\u5305\u542B\u4EFB\u52A1\u3001\u9875\u9762\u3001\u4EFB\u52A1\u5173\u7CFB\u3001\u65B0\u9875\u5E38\u7528\u7F51\u7AD9\u548C\u7F51\u9875\u62D3\u5C55\u575E\uFF1B\u4E0D\u5305\u542B API \u5BC6\u94A5\u3001\u6FC0\u6D3B\u4FE1\u606F\u3001\u622A\u56FE\u3001Cookie\u3001\u884C\u4E3A\u539F\u59CB\u4E0A\u4E0B\u6587\u6216\u5B8C\u6574\u7F51\u5740\u53C2\u6570\u3002")}</div><div class="dock-site-empty" data-behavior-clear-status role="status" aria-live="polite" hidden></div><div class="deactivation-error" data-behavior-clear-error role="alert" hidden></div><div class="deactivation-actions"><button class="btn" type="button" data-v2-data-action="export">${tr("privacy.export", "\u5B89\u5168\u5BFC\u51FA")}</button><button class="btn deactivation-danger" type="button" data-clear-behavior-history>${tr("privacy.clearBehavior", "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9")}</button><button class="btn deactivation-danger" type="button" data-v2-data-action="clear-ai">${tr("privacy.clearAi", "\u6E05\u9664 AI \u6570\u636E")}</button><button class="btn deactivation-danger" type="button" data-v2-data-action="clear-all">${tr("privacy.clearAll", "\u6E05\u9664\u5168\u90E8\u672C\u5730\u6570\u636E")}</button></div></section></div>`;
            content.querySelector("[data-privacy-data-settings]")?.insertAdjacentHTML("afterbegin", `<section class="dock-site-section"><h3>${tr("settings.languageTitle", "\u754C\u9762\u8BED\u8A00")}</h3><div class="dock-site-empty">${tr("settings.languageHint", "\u9ED8\u8BA4\u8DDF\u968F\u6D4F\u89C8\u5668\uFF0C\u4E5F\u53EF\u4EE5\u53EA\u4E3A TabFlow \u624B\u52A8\u5207\u6362\u3002")}</div><label><span class="sr-only">${tr("settings.languageTitle", "\u754C\u9762\u8BED\u8A00")}</span><select class="input" id="settingsLanguagePreference" data-language-preference><option value="system">${tr("settings.languageSystem", "\u8DDF\u968F\u6D4F\u89C8\u5668")}</option><option value="zh-CN">${tr("settings.languageChinese", "\u4E2D\u6587")}</option><option value="en">${tr("settings.languageEnglish", "English")}</option></select></label><div class="deactivation-error" data-language-error role="alert" hidden></div></section>`);
            const languageSelect = content.querySelector("[data-language-preference]"), languageError = content.querySelector("[data-language-error]");
            if (languageSelect && typeof loadLanguagePreference === "function") void loadLanguagePreference().then((value) => {
              if (languageSelect.isConnected) languageSelect.value = value;
            }).catch(() => {
            });
            languageSelect?.addEventListener("change", async () => {
              languageSelect.disabled = true;
              languageError.hidden = true;
              try {
                await saveLanguagePreference?.(languageSelect.value);
                refreshLanguage();
              } catch (_error) {
                languageSelect.disabled = false;
                languageError.textContent = tr("settings.languageSaveFailed", "\u8BED\u8A00\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
                languageError.hidden = false;
              }
            });
            content.querySelector("[data-clear-behavior-history]").addEventListener("click", clearBehaviorHistory);
            content.querySelector('[data-v2-data-action="export"]').addEventListener("click", exportSafeData);
            content.querySelector('[data-v2-data-action="clear-ai"]').addEventListener("click", clearAiHistory);
            content.querySelector('[data-v2-data-action="clear-all"]').addEventListener("click", clearEverything);
            backupSection.render();
            syncPrivacyDataControls();
          }
          function renderWebDockSettings() {
            if (!settingsDialog || settingsSection !== "web-dock") return;
            const content = settingsDialog.querySelector("#settingsContent");
            if (!content) return;
            const enabled = context().config.enabled;
            const visualDraft = ensureDockVisualDraft();
            const siteMarkup = context().config.sites.length ? context().config.sites.map(dockSiteRowMarkup).join("") : `<div class="dock-site-empty">${escapeHtml(tr("dock.settings.empty", "\u6682\u65E0\u56FA\u5B9A\u7F51\u7AD9"))}</div>`;
            const motionControls = [
              { key: "iconSize", label: tr("dock.settings.iconSize", "\u56FE\u6807\u5927\u5C0F"), attribute: 'data-dock-motion-key="iconSize"' },
              { key: "magnification", label: tr("dock.settings.magnification", "\u60AC\u505C\u653E\u5927"), attribute: 'data-dock-motion-key="magnification"' },
              { key: "influenceRadius", label: tr("dock.settings.influenceRadius", "\u5F71\u54CD\u8303\u56F4"), attribute: 'data-dock-motion-key="influenceRadius"' },
              { key: "response", label: tr("dock.settings.response", "\u54CD\u5E94\u901F\u5EA6"), attribute: 'data-dock-motion-key="response"' },
              { key: "damping", label: tr("dock.settings.damping", "\u963B\u5C3C"), attribute: 'data-dock-motion-key="damping"' },
              { key: "hideDelay", label: tr("dock.settings.hideDelay", "\u9690\u85CF\u5EF6\u8FDF"), attribute: 'data-dock-motion-key="hideDelay"' }
            ];
            const appearanceControls = [
              { key: "tileSize", label: tr("dock.settings.tileSize", "\u56FE\u6807\u5E95\u677F\u5927\u5C0F") },
              { key: "trayPadding", label: tr("dock.settings.trayPadding", "\u6258\u76D8\u7559\u767D") },
              { key: "tileOpacity", label: tr("dock.settings.tileOpacity", "\u5E95\u677F\u4E0D\u900F\u660E\u5EA6") }
            ];
            const motionMarkup = motionControls.map(({ key, label, attribute }) => {
              const limits = MOTION_LIMITS[key];
              const value = visualDraft.motion[key];
              return `<div class="dock-motion-control"><label for="dockMotion_${key}">${escapeHtml(label)}</label><input id="dockMotion_${key}" ${attribute} type="range" min="${limits[0]}" max="${limits[1]}" step="${motionStep(key)}" value="${value}"><output id="dockMotionOutput_${key}" for="dockMotion_${key}">${value}</output></div>`;
            }).join("");
            const appearanceMarkup = appearanceControls.map(({ key, label }) => {
              const limits = APPEARANCE_LIMITS[key];
              const value = visualDraft.appearance[key];
              return `<div class="dock-motion-control dock-appearance-control"><label for="dockAppearance_${key}">${escapeHtml(label)}</label><input id="dockAppearance_${key}" data-dock-appearance-key="${key}" type="range" min="${limits[0]}" max="${limits[1]}" step="1" value="${value}"><output id="dockAppearanceOutput_${key}" for="dockAppearance_${key}">${formatDockAppearanceOutput(key, value)}</output></div>`;
            }).join("");
            const tileColor = normalizeDockColor(visualDraft.appearance.tileColor);
            const appearanceColorMarkup = `<div class="dock-motion-control dock-appearance-control dock-color-control"><label for="dockAppearance_tileColor">${escapeHtml(tr("dock.settings.tileColor", "\u56FE\u6807\u5E95\u677F\u989C\u8272"))}</label><input id="dockAppearance_tileColor" data-dock-appearance-color-key="tileColor" type="color" value="${tileColor}" aria-label="${escapeHtml(tr("dock.settings.tileColor", "\u56FE\u6807\u5E95\u677F\u989C\u8272"))}"><output id="dockAppearanceColorOutput_tileColor" for="dockAppearance_tileColor">${tileColor.toUpperCase()}</output></div>`;
            const previewSites = context().config.sites.length ? context().config.sites : DOCK_PREVIEW_FALLBACK_SITES;
            const previewMarkup = previewSites.map((site) => dockItemMarkup(site, { preview: true })).join("");
            content.innerHTML = `
        <div class="web-dock-settings">
          <div class="dock-settings-error" id="dockSettingsError" role="alert" ${dockSettingsError ? "" : "hidden"}>${escapeHtml(dockSettingsError)}</div>
          <section class="dock-toggle-section">
            <button id="dockEnabledToggle" type="button" role="switch" aria-checked="${enabled}">
              <span class="dock-switch-copy"><strong>${escapeHtml(tr("dock.settings.enabled", "\u542F\u7528\u7F51\u9875\u62D3\u5C55\u575E"))}</strong><small>${escapeHtml(tr("dock.settings.enabledHint", "\u5728\u6240\u6709\u7F51\u9875\u5E95\u90E8\u663E\u793A\u5E38\u7528\u7F51\u7AD9"))}</small></span>
              <span class="dock-switch-track" aria-hidden="true"><span class="dock-switch-thumb"></span></span>
            </button>
            <button id="dockAutoHideToggle" type="button" role="switch" aria-checked="${context().config.autoHide}" ${enabled ? "" : 'disabled aria-disabled="true"'}>
              <span class="dock-switch-copy"><strong>${escapeHtml(tr("dock.settings.autoHide", "\u81EA\u52A8\u9690\u85CF"))}</strong><small>${escapeHtml(tr("dock.settings.autoHideHint", "\u9F20\u6807\u79BB\u5F00\u540E\u81EA\u52A8\u6536\u8D77\u62D3\u5C55\u575E"))}</small></span>
              <span class="dock-switch-track" aria-hidden="true"><span class="dock-switch-thumb"></span></span>
            </button>
          </section>
          <section class="dock-appearance-section">
            <h3>${escapeHtml(tr("dock.settings.appearance", "\u5916\u89C2"))}</h3>
            <div class="dock-motion-controls dock-appearance-controls">${appearanceMarkup}${appearanceColorMarkup}</div>
          </section>
          <section class="dock-motion-section">
            <h3>${escapeHtml(tr("dock.settings.motion", "\u52A8\u6548"))}</h3>
            <div class="dock-motion-controls">${motionMarkup}</div>
            <div class="dock-preview-heading"><span>${escapeHtml(tr("dock.settings.preview", "\u9884\u89C8"))}</span><button id="dockMotionReset" type="button">${escapeHtml(tr("dock.settings.reset", "\u6062\u590D\u9ED8\u8BA4"))}</button></div>
            <div id="dockMotionPreview" aria-label="${escapeHtml(tr("dock.settings.previewAria", "\u7F51\u9875\u62D3\u5C55\u575E\u9884\u89C8"))}"><div class="web-dock-track dock-preview-track">${previewMarkup}</div></div>
            <div class="dock-motion-error" id="dockMotionError" role="alert" hidden></div>
          </section>
          <section class="dock-site-section">
            <h3>${escapeHtml(tr("dock.settings.fixedSites", "\u56FA\u5B9A\u7F51\u7AD9"))}</h3>
            <div id="dockSiteList">${siteMarkup}</div>
            <form id="dockSiteForm" novalidate>
              <label for="dockSiteName">${escapeHtml(tr("dock.settings.siteName", "\u7F51\u7AD9\u540D\u79F0"))}</label>
              <input id="dockSiteName" name="title" type="text" value="${escapeHtml(dockSiteDraft.title)}" aria-label="${escapeHtml(tr("dock.settings.siteName", "\u7F51\u7AD9\u540D\u79F0"))}">
              <label for="dockSiteUrl">${escapeHtml(tr("dock.settings.siteUrl", "\u7F51\u7AD9\u5730\u5740"))}</label>
              <input id="dockSiteUrl" name="url" type="text" value="${escapeHtml(dockSiteDraft.url)}" aria-label="${escapeHtml(tr("dock.settings.siteUrl", "\u7F51\u7AD9\u5730\u5740"))}">
              <div id="dockSiteError" role="alert" ${dockSiteError ? "" : "hidden"}>${escapeHtml(dockSiteError)}</div>
              <button type="submit">${escapeHtml(tr("dock.settings.addSite", "\u6DFB\u52A0\u7F51\u7AD9"))}</button>
            </form>
          </section>
          <p class="dock-platform-note">${escapeHtml(tr("dock.settings.platformNote", "\u63D0\u793A\uFF1A\u90E8\u5206\u7F51\u7AD9\u4F1A\u9650\u5236\u8BFB\u53D6\u56FE\u6807\uFF0CTabFlow \u4F1A\u81EA\u52A8\u4F7F\u7528\u5B89\u5168\u66FF\u4EE3\u56FE\u6807\u3002"))}</p>
          <footer class="dock-settings-save-bar">
            <span data-dock-settings-status>${escapeHtml(tr("dock.settings.saveHint", "\u4FEE\u6539\u4F1A\u5B9E\u65F6\u9884\u89C8\uFF0C\u70B9\u51FB\u5B8C\u6210\u540E\u5173\u95ED\u8BBE\u7F6E\u3002"))}</span>
            <button class="dock-settings-save" type="button" data-dock-settings-done><span class="dock-settings-save-icon" aria-hidden="true">\u2713</span>${escapeHtml(tr("dock.settings.done", "\u5B8C\u6210"))}</button>
          </footer>
        </div>`;
            restoreRowStates();
            bindDockSettingsFaviconFallbacks();
            bindWebDockSettingsEvents();
            bindDockFaviconElements(content.querySelector("#dockMotionPreview"));
            initializeDockPreviewAnimation();
          }
          function syncDockSwitchControl(button, checked, disabled = false) {
            if (!button) return;
            button.setAttribute("aria-checked", String(checked));
            button.disabled = disabled;
            if (disabled) {
              button.setAttribute("disabled", "");
              button.setAttribute("aria-disabled", "true");
            } else {
              button.removeAttribute("disabled");
              button.removeAttribute("aria-disabled");
            }
          }
          function syncWebDockSettingsControls(options = {}) {
            if (!settingsDialog || settingsSection !== "web-dock") return;
            syncDockSwitchControl(settingsDialog.querySelector("#dockEnabledToggle"), context().config.enabled);
            syncDockSwitchControl(settingsDialog.querySelector("#dockAutoHideToggle"), context().config.autoHide, !context().config.enabled);
            if (options.syncVisual !== true) return;
            const draft = ensureDockVisualDraft();
            settingsDialog.querySelectorAll("[data-dock-motion-key]").forEach((input) => {
              const key = input.dataset.dockMotionKey;
              input.value = String(draft.motion[key]);
              const output = settingsDialog.querySelector(`#dockMotionOutput_${key}`);
              if (output) output.textContent = String(draft.motion[key]);
            });
            settingsDialog.querySelectorAll("[data-dock-appearance-key]").forEach((input) => {
              const key = input.dataset.dockAppearanceKey;
              input.value = String(draft.appearance[key]);
              const output = settingsDialog.querySelector(`#dockAppearanceOutput_${key}`);
              if (output) output.textContent = formatDockAppearanceOutput(key, draft.appearance[key]);
            });
            settingsDialog.querySelectorAll("[data-dock-appearance-color-key]").forEach((input) => {
              const key = input.dataset.dockAppearanceColorKey;
              const color = normalizeDockColor(draft.appearance[key]);
              input.value = color;
              const output = settingsDialog.querySelector(`#dockAppearanceColorOutput_${key}`);
              if (output) output.textContent = color.toUpperCase();
            });
            const error = settingsDialog.querySelector("#dockMotionError");
            if (options.clearError === true && error) {
              error.textContent = "";
              error.hidden = true;
            }
            const preview = settingsDialog.querySelector("#dockMotionPreview");
            if (preview && !previewRuntime.isConnectedTo(preview)) {
              bindDockFaviconElements(preview);
              initializeDockPreviewAnimation();
            } else renderDockMotionPreview();
          }
          function showDockControlError(element, message) {
            if (element) {
              element.textContent = message;
              element.hidden = false;
            }
          }
          const updateDockSetting = settingsRuntime.updateSetting;
          async function saveDockSiteDraft(siteId = "", delegatedRow = null) {
            if (!settingsDialog) return false;
            let titleInput, urlInput, error, editedRow = null;
            if (siteId) {
              const row = delegatedRow || Array.from(settingsDialog.querySelectorAll("[data-dock-site-id]")).find((candidate) => candidate.dataset.dockSiteId === siteId);
              if (!row) return false;
              editedRow = row;
              titleInput = row.querySelector("[data-dock-site-title]");
              urlInput = row.querySelector("[data-dock-site-url]");
              error = row.querySelector("[data-dock-site-error]");
            } else {
              titleInput = settingsDialog.querySelector("#dockSiteName");
              urlInput = settingsDialog.querySelector("#dockSiteUrl");
              error = settingsDialog.querySelector("#dockSiteError");
              dockSiteDraft = { title: titleInput ? titleInput.value : "", url: urlInput ? urlInput.value : "" };
            }
            let site = { ...siteId ? { id: siteId } : {}, title: titleInput ? titleInput.value.trim() : "", url: urlInput ? urlInput.value.trim() : "" };
            let result;
            const editIdentity = siteId ? rowIdentity(editedRow) : null;
            try {
              if (siteId) {
                if (!settingsRuntime.getRowDraft(editIdentity)) settingsRuntime.updateRowDraft(editIdentity, site);
                const outcome = await settingsRuntime.saveRowDraft(editIdentity, (draft) => persistWebDockSite({
                  id: draft.siteId || siteId,
                  title: draft.title,
                  url: draft.url
                }, { toastMessage: tr("dock.settings.updated", "\u7F51\u7AD9\u5DF2\u66F4\u65B0") }));
                result = outcome.result || { ok: false };
                site = outcome.draft || site;
                restoreRowStates();
                if (outcome.ok && !outcome.committed) return true;
              } else {
                result = await persistWebDockSite(site, { toastMessage: tr("dock.settings.added", "\u7F51\u7AD9\u5DF2\u6DFB\u52A0") });
              }
            } catch (caughtError) {
              const message2 = caughtError && caughtError.message ? caughtError.message : siteId ? tr("dock.settings.saveFailed", "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5") : tr("dock.settings.addFailed", "\u6DFB\u52A0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
              if (!siteId) dockSiteError = message2;
              const currentDialog = settingsDialog;
              const currentError = siteId ? findRow(settingsDialog, editIdentity)?.querySelector("[data-dock-site-error]") : currentDialog && currentDialog.querySelector("#dockSiteError");
              showDockControlError(currentError || error, message2);
              return false;
            }
            if (result.ok && result.current !== false) {
              dockSiteDraft = { title: "", url: "" };
              dockSiteError = "";
              if (siteId && settingsDialog) {
                const currentRow = findRow(settingsDialog, editIdentity);
                if (currentRow) delete currentRow.dataset.dockSiteDirty;
              }
              return true;
            }
            if (result.ok) return true;
            const message = dockResponseMessage(result.response, siteId ? tr("dock.settings.saveFailed", "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5") : tr("dock.settings.addFailed", "\u6DFB\u52A0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"));
            if (!siteId) dockSiteError = message;
            showDockControlError(error, message);
            return false;
          }
          function bindWebDockSettingsEvents() {
            if (!settingsDialog || settingsSection !== "web-dock") return;
            const enabledToggle = settingsDialog.querySelector("#dockEnabledToggle");
            const autoHideToggle = settingsDialog.querySelector("#dockAutoHideToggle");
            const siteForm = settingsDialog.querySelector("#dockSiteForm");
            const siteList = settingsDialog.querySelector("#dockSiteList");
            const preview = settingsDialog.querySelector("#dockMotionPreview");
            enabledToggle.addEventListener("click", () => updateDockSetting("enabled", !context().config.enabled));
            autoHideToggle.addEventListener("click", () => {
              if (context().config.enabled) updateDockSetting("autoHide", !context().config.autoHide);
            });
            siteForm.addEventListener("submit", (event) => {
              event.preventDefault();
              saveDockSiteDraft();
            });
            siteForm.querySelectorAll("input").forEach((input) => input.addEventListener("input", () => {
              dockSiteDraft = {
                title: settingsDialog.querySelector("#dockSiteName").value,
                url: settingsDialog.querySelector("#dockSiteUrl").value
              };
              dockSiteError = "";
              const error = settingsDialog.querySelector("#dockSiteError");
              if (error) error.hidden = true;
            }));
            siteList.addEventListener("input", (event) => {
              const input = event.target.closest("[data-dock-site-title], [data-dock-site-url]");
              const row = input && input.closest("[data-dock-site-id]");
              if (!row) return;
              settingsRuntime.updateRowDraft(rowIdentity(row), {
                title: row.querySelector("[data-dock-site-title]")?.value || "",
                url: row.querySelector("[data-dock-site-url]")?.value || ""
              });
              row.dataset.dockSiteDirty = "true";
              const error = row.querySelector("[data-dock-site-error]");
              if (error) {
                error.textContent = "";
                error.hidden = true;
              }
            });
            siteList.addEventListener("click", (event) => {
              const row = event.target.closest("[data-dock-site-id]");
              if (!row) return;
              const error = row.querySelector("[data-dock-site-error]");
              if (event.target.closest("[data-dock-site-save]")) saveDockSiteDraft(row.dataset.dockSiteId, row);
              if (event.target.closest("[data-dock-site-delete]")) removeDockSite(row.dataset.dockSiteId, error);
            });
            siteList.addEventListener("keydown", (event) => {
              if (event.key !== "Enter") return;
              const input = event.target.closest("[data-dock-site-title], [data-dock-site-url]");
              if (!input) return;
              event.preventDefault();
              const row = input.closest("[data-dock-site-id]");
              if (row) saveDockSiteDraft(row.dataset.dockSiteId, row);
            });
            const clearSettingsDockDropState = () => {
              siteList.querySelectorAll(".dock-site-row").forEach((row) => {
                row.classList.remove("is-dragging", "is-drop-before", "is-drop-after");
                row.style.removeProperty("transform");
              });
            };
            const markSettingsDockDropTarget = (target, clientY) => {
              siteList.querySelectorAll(".is-drop-before, .is-drop-after").forEach((row) => {
                row.classList.remove("is-drop-before", "is-drop-after");
              });
              if (!target) return;
              const rect = target.getBoundingClientRect();
              target.classList.add(clientY >= rect.top + rect.height / 2 ? "is-drop-after" : "is-drop-before");
            };
            const animateSettingsDockOrder = (orderedUrls, beforeRects) => {
              const rows = Array.from(siteList.querySelectorAll("[data-dock-site-id]"));
              const before = beforeRects || new Map(rows.map((row) => [row.dataset.dockSiteUrl, row.getBoundingClientRect().top]));
              const byUrl = new Map(rows.map((row) => [row.dataset.dockSiteUrl, row]));
              orderedUrls.forEach((url, index) => {
                const row = byUrl.get(url);
                if (!row) return;
                row.dataset.dockSiteIndex = String(index);
                siteList.appendChild(row);
              });
              Array.from(siteList.querySelectorAll("[data-dock-site-id]")).forEach((row) => {
                const previousTop = before.get(row.dataset.dockSiteUrl);
                const delta = Number.isFinite(previousTop) ? previousTop - row.getBoundingClientRect().top : 0;
                if (!delta || typeof row.animate !== "function") return;
                row.animate([
                  { transform: `translate3d(0, ${delta}px, 0)` },
                  { transform: "translate3d(0, 0, 0)" }
                ], { duration: 220, easing: "cubic-bezier(.22,1,.36,1)" });
              });
            };
            const finishSettingsDockSiteDrop = (draggedSite, target, clientY, beforeRects) => {
              if (!target || !draggedSite || target.dataset.dockSiteUrl === draggedSite.url) return false;
              const sourceAtIndex = context().config.sites[draggedSite.index];
              const fromIndex = sourceAtIndex && sourceAtIndex.url === draggedSite.url ? draggedSite.index : context().config.sites.findIndex((site) => site.url === draggedSite.url);
              const rawTargetIndex = Number(target.dataset.dockSiteIndex);
              const targetAtIndex = context().config.sites[rawTargetIndex];
              const targetIndex = targetAtIndex && targetAtIndex.url === target.dataset.dockSiteUrl ? rawTargetIndex : context().config.sites.findIndex((site) => site.url === target.dataset.dockSiteUrl);
              if (fromIndex < 0 || targetIndex < 0) return false;
              const rect = target.getBoundingClientRect();
              const dropAfter = Number.isFinite(clientY) && clientY >= rect.top + rect.height / 2;
              const adjustedTargetIndex = targetIndex - (fromIndex < targetIndex ? 1 : 0);
              const insertionIndex = adjustedTargetIndex + (dropAfter ? 1 : 0);
              const orderedUrls = moveDockSiteUrls(context().config.sites, fromIndex, insertionIndex);
              const originalConfig = context().config;
              const optimisticConfig = reorderDockSitesByUrls(originalConfig, orderedUrls);
              animateSettingsDockOrder(orderedUrls, beforeRects);
              setOptimisticConfig(optimisticConfig);
              renderWebDock();
              persistDockSiteOrder(orderedUrls).then((result) => {
                if (!result.saved && result.current) applyWebDockConfig(originalConfig, { updateConfirmedBase: false });
              });
              return true;
            };
            const settingsDockSiteRowAtY = (clientY, excludedRow = null) => {
              const rows = Array.from(siteList.querySelectorAll("[data-dock-site-id]")).filter((row) => row !== excludedRow && !row.classList.contains("is-dragging"));
              if (!rows.length || !Number.isFinite(clientY)) return null;
              return rows.find((row) => {
                const rect = row.getBoundingClientRect();
                return clientY >= rect.top && clientY <= rect.bottom;
              }) || rows.reduce((nearest, row) => {
                const rect = row.getBoundingClientRect();
                const distance = Math.min(Math.abs(clientY - rect.top), Math.abs(clientY - rect.bottom));
                return !nearest || distance < nearest.distance ? { row, distance } : nearest;
              }, null)?.row || null;
            };
            let settingsDockPointerDrag = null;
            siteList.addEventListener("pointerdown", (event) => {
              const handle = event.target.closest(".dock-site-drag-handle");
              const row = handle && handle.closest("[data-dock-site-id]");
              if (!row || Number.isFinite(event.button) && event.button !== 0) return;
              event.preventDefault();
              settingsDockPointerDrag = {
                pointerId: event.pointerId,
                url: row.dataset.dockSiteUrl,
                index: Number(row.dataset.dockSiteIndex),
                startY: Number(event.clientY),
                clientY: Number(event.clientY),
                moved: false,
                row
              };
              row.classList.add("is-dragging");
              try {
                siteList.setPointerCapture(event.pointerId);
              } catch (_error) {
              }
            });
            siteList.addEventListener("pointermove", (event) => {
              if (!settingsDockPointerDrag || event.pointerId !== settingsDockPointerDrag.pointerId) return;
              const clientY = Number(event.clientY);
              if (!Number.isFinite(clientY)) return;
              settingsDockPointerDrag.clientY = clientY;
              if (Math.abs(clientY - settingsDockPointerDrag.startY) >= 4) {
                settingsDockPointerDrag.moved = true;
                event.preventDefault();
                settingsDockPointerDrag.row.style.transform = `translate3d(0, ${clientY - settingsDockPointerDrag.startY}px, 0)`;
                const target = settingsDockSiteRowAtY(clientY, settingsDockPointerDrag.row);
                if (target && target !== settingsDockPointerDrag.row) markSettingsDockDropTarget(target, clientY);
              }
            });
            const finishSettingsDockPointerDrag = (event, cancelled = false) => {
              const draggedSite = settingsDockPointerDrag;
              if (!draggedSite || event.pointerId !== draggedSite.pointerId) return;
              settingsDockPointerDrag = null;
              try {
                siteList.releasePointerCapture(event.pointerId);
              } catch (_error) {
              }
              const clientY = Number.isFinite(Number(event.clientY)) ? Number(event.clientY) : draggedSite.clientY;
              const target = settingsDockSiteRowAtY(clientY, draggedSite.row);
              if (cancelled || !draggedSite.moved || !target) {
                clearSettingsDockDropState();
                return;
              }
              event.preventDefault();
              const beforeRects = new Map(Array.from(siteList.querySelectorAll("[data-dock-site-id]")).map((row) => [row.dataset.dockSiteUrl, row.getBoundingClientRect().top]));
              clearSettingsDockDropState();
              finishSettingsDockSiteDrop(draggedSite, target, clientY, beforeRects);
            };
            siteList.addEventListener("pointerup", (event) => finishSettingsDockPointerDrag(event));
            siteList.addEventListener("pointercancel", (event) => finishSettingsDockPointerDrag(event, true));
            settingsDialog.querySelectorAll("[data-dock-motion-key]").forEach((input) => {
              const updatePreview = () => {
                const key = input.dataset.dockMotionKey;
                const limits = MOTION_LIMITS[key];
                const numeric = Number(input.value);
                const clamped = Math.min(limits[1], Math.max(limits[0], Number.isFinite(numeric) ? numeric : context().config.motion[key]));
                input.value = String(clamped);
                updateDockVisualDraft("motion", key, clamped);
                const output = settingsDialog.querySelector(`#dockMotionOutput_${key}`);
                if (output) output.textContent = String(clamped);
                const error = settingsDialog.querySelector("#dockMotionError");
                if (error) {
                  error.textContent = "";
                  error.hidden = true;
                }
                renderDockMotionPreview();
                queueDockVisualPersistence("motion", key, clamped);
              };
              input.addEventListener("input", updatePreview);
              input.addEventListener("change", () => {
                updatePreview();
                flushDockVisualPersistence();
              });
            });
            settingsDialog.querySelectorAll("[data-dock-appearance-key]").forEach((input) => {
              const updateAppearance = () => {
                const key = input.dataset.dockAppearanceKey;
                const limits = APPEARANCE_LIMITS[key];
                const numeric = Number(input.value);
                const clamped = Math.min(
                  limits[1],
                  Math.max(limits[0], Number.isFinite(numeric) ? numeric : context().config.appearance[key])
                );
                input.value = String(clamped);
                updateDockVisualDraft("appearance", key, clamped);
                const output = settingsDialog.querySelector(`#dockAppearanceOutput_${key}`);
                if (output) output.textContent = formatDockAppearanceOutput(key, clamped);
                const error = settingsDialog.querySelector("#dockMotionError");
                if (error) {
                  error.textContent = "";
                  error.hidden = true;
                }
                renderDockMotionPreview();
                queueDockVisualPersistence("appearance", key, clamped);
              };
              input.addEventListener("input", updateAppearance);
              input.addEventListener("change", () => {
                updateAppearance();
                flushDockVisualPersistence();
              });
            });
            settingsDialog.querySelectorAll("[data-dock-appearance-color-key]").forEach((input) => {
              const updateAppearanceColor = () => {
                const key = input.dataset.dockAppearanceColorKey;
                const color = normalizeDockColor(input.value, context().config.appearance[key]);
                input.value = color;
                updateDockVisualDraft("appearance", key, color);
                const output = settingsDialog.querySelector(`#dockAppearanceColorOutput_${key}`);
                if (output) output.textContent = color.toUpperCase();
                const error = settingsDialog.querySelector("#dockMotionError");
                if (error) {
                  error.textContent = "";
                  error.hidden = true;
                }
                renderDockMotionPreview();
                queueDockVisualPersistence("appearance", key, color);
              };
              input.addEventListener("input", updateAppearanceColor);
              input.addEventListener("change", () => {
                updateAppearanceColor();
                flushDockVisualPersistence();
              });
            });
            preview.addEventListener("pointermove", (event) => previewRuntime.pointerMove(event.clientX));
            preview.addEventListener("pointerleave", () => previewRuntime.pointerLeave());
            settingsDialog.querySelector("#dockMotionReset").addEventListener("click", resetDockMotionSettings);
            settingsDialog.querySelector("[data-dock-settings-done]").addEventListener("click", () => {
              const dirtyRow = Array.from(settingsDialog.querySelectorAll("[data-dock-site-id]")).find((row) => row.dataset.dockSiteDirty === "true");
              if (!dirtyRow) {
                closeSettingsDialog();
                return;
              }
              showDockControlError(dirtyRow.querySelector("[data-dock-site-error]"), tr("dock.settings.saveDirtyFirst", "\u8BF7\u5148\u4FDD\u5B58\u5F53\u524D\u7F16\u8F91\u9879"));
              const saveButton = dirtyRow.querySelector("[data-dock-site-save]");
              if (saveButton) saveButton.focus({ preventScroll: true });
            });
          }
          function openSettingsDialog(initialSection = "activation", restoreFocusTarget = null) {
            if (destroyed) return false;
            ensureHost();
            const requestedSection = ["web-dock", "privacy-data", "ai"].includes(initialSection) ? initialSection : "activation";
            if (settingsDialog) {
              if (restoreFocusTarget) settingsDialogRestoreFocus = restoreFocusTarget;
              selectSettingsSection(requestedSection);
              const focusTarget = requestedSection === "web-dock" ? settingsDialog.querySelector("#dockEnabledToggle") : requestedSection === "ai" ? aiSection.focusTarget() : settingsDialog.querySelector("#settingsDialogClose");
              if (focusTarget) focusTarget.focus({ preventScroll: true });
              return;
            }
            settingsSection = requestedSection;
            settingsDialogRestoreFocus = restoreFocusTarget || context().settingsButton || null;
            const activationActive = settingsSection === "activation", dockActive = settingsSection === "web-dock", privacyActive = settingsSection === "privacy-data", aiActive = settingsSection === "ai";
            const shell = document2.createElement("div");
            shell.className = "settings-dialog-shell";
            shell.innerHTML = `
        <div class="settings-dialog-overlay" data-settings-dismiss></div>
        <section class="settings-dialog" id="settingsDialog" role="dialog" aria-modal="true" aria-labelledby="settingsDialogTitle">
          <aside class="settings-sidebar" aria-label="${escapeHtml(tr("settings.navigation", "\u8BBE\u7F6E\u5BFC\u822A"))}">
            <div class="settings-sidebar-title" data-settings-copy="title">${escapeHtml(tr("settings.title", "\u8BBE\u7F6E"))}</div>
            <button class="settings-nav-item${activationActive ? " active" : ""}" type="button" data-settings-section="activation" ${activationActive ? 'aria-current="page"' : ""}>
              <span class="settings-nav-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 3l7 7l-7 11l-7-11z"/><path d="M5 10h14"/></svg></span><span data-settings-label="activation">${escapeHtml(tr("settings.activationTitle", "\u6C38\u4E45\u6FC0\u6D3B"))}</span>
            </button>
            <button class="settings-nav-item${dockActive ? " active" : ""}" type="button" data-settings-section="web-dock" ${dockActive ? 'aria-current="page"' : ""}>
              <span class="settings-nav-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 16h10"/><path d="M8 12h2M12 12h2M16 12h1"/></svg></span><span data-settings-label="web-dock">${escapeHtml(tr("settings.dockTitle", "\u7F51\u9875\u62D3\u5C55\u575E"))}</span>
            </button>
            <button class="settings-nav-item${aiActive ? " active" : ""}" type="button" data-settings-section="ai" ${aiActive ? 'aria-current="page"' : ""}>
              <span class="settings-nav-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5z"/><path d="M18 15l.8 2.2L21 18l-2.2.8L18 21l-.8-2.2L15 18l2.2-.8z"/></svg></span><span data-settings-label="ai">${escapeHtml(tr("ai.settings.nav", "AI \u667A\u80FD\u6574\u7406"))}</span>
            </button>
            <button class="settings-nav-item${privacyActive ? " active" : ""}" type="button" data-settings-section="privacy-data" ${privacyActive ? 'aria-current="page"' : ""}><span class="settings-nav-icon" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 3l7 3v5c0 4.6-2.8 8-7 10-4.2-2-7-5.4-7-10V6z"/><path d="M9 12l2 2 4-4"/></svg></span><span data-settings-label="privacy-data">${tr("privacy.title", "\u9690\u79C1\u4E0E\u6570\u636E")}</span></button>
          </aside>
          <div class="settings-main">
            <header class="settings-dialog-header">
              <div>
                <div class="activation-eyebrow">Settings</div>
                <h2 id="settingsDialogTitle">${escapeHtml(tr("settings.activationTitle", "\u6C38\u4E45\u6FC0\u6D3B"))}</h2>
                <p id="settingsDialogDescription">${escapeHtml(tr("settings.activationDescription", "\u6FC0\u6D3B\u589E\u503C\u6743\u76CA\uFF0C\u514D\u8D39\u6838\u5FC3\u529F\u80FD\u59CB\u7EC8\u53EF\u7528"))}</p>
              </div>
              <button class="settings-dialog-close" id="settingsDialogClose" type="button" aria-label="${escapeHtml(tr("settings.close", "\u5173\u95ED\u8BBE\u7F6E"))}">\xD7</button>
            </header>
            <div id="settingsContent"></div>
          </div>
        </section>
      `;
            context().root.appendChild(shell);
            settingsDialog = shell;
            const openLifecycle = ++lifecycle;
            syncHostPointerInteractivity();
            shell.addEventListener("wheel", (event) => event.stopPropagation(), { passive: true });
            shell.addEventListener("keydown", (event) => {
              if (event.key !== "Escape") return;
              if (settingsSection === "privacy-data") {
                if (backupPending) return;
                event.preventDefault();
                if (backupSection.inspect().hasBufferedFile) backupSection.cancel();
                else closeSettingsDialog();
                return;
              }
              if (settingsSection !== "ai") return;
              event.preventDefault();
              closeSettingsDialog();
            });
            shell.querySelector("#settingsDialogClose").addEventListener("click", () => closeSettingsDialog());
            shell.querySelector("[data-settings-dismiss]").addEventListener("click", () => closeSettingsDialog());
            shell.querySelectorAll("[data-settings-section]").forEach((button) => button.addEventListener("click", () => selectSettingsSection(button.dataset.settingsSection)));
            selectSettingsSection(settingsSection);
            openFrame = requestFrame(() => {
              openFrame = null;
              if (!destroyed && lifecycle === openLifecycle && settingsDialog === shell) shell.classList.add("show");
            });
            focusTimer = setTimeoutFn(() => {
              focusTimer = null;
              if (destroyed || lifecycle !== openLifecycle || settingsDialog !== shell) return;
              const focusTarget = settingsSection === "web-dock" ? shell.querySelector("#dockEnabledToggle") : settingsSection === "ai" ? aiSection.focusTarget() : shell.querySelector("#settingsDialogClose");
              if (focusTarget) focusTarget.focus({ preventScroll: true });
            }, 0);
            if (settingsSection === "activation") refreshLicenseState();
          }
          function closeSettingsDialog(restoreFocus = true, force = false) {
            if (!settingsDialog || !force && (activationRuntime.isDeactivationPending() || backupPending)) return false;
            cancelOpenEffects();
            if (settingsSection === "web-dock") {
              flushDockVisualPersistence();
              stopDockPreviewAnimation();
            }
            closePurchasePreview(false);
            closeLicenseDeactivationDialog(false);
            const shell = settingsDialog;
            settingsDialog = null;
            aiSection.reset();
            backupSection.reset();
            backupPending = false;
            settingsRuntime.clearVisualDraft();
            settingsRuntime.clearRowDrafts();
            shell.classList.remove("show");
            shell.remove();
            syncHostPointerInteractivity();
            const focusTarget = settingsDialogRestoreFocus;
            settingsDialogRestoreFocus = null;
            if (restoreFocus && focusTarget && focusTarget.isConnected && typeof focusTarget.focus === "function") {
              if (focusTarget.classList && focusTarget.classList.contains("web-dock-control")) setWebDockExpanded(true);
              focusTarget.focus();
            }
            return true;
          }
          function getDialog() {
            return settingsDialog;
          }
          function getSection() {
            return settingsSection;
          }
          function isOpen() {
            return Boolean(settingsDialog);
          }
          function forceClose(restoreFocus = false) {
            return closeSettingsDialog(restoreFocus, true);
          }
          function refreshLanguage() {
            if (!settingsDialog) return false;
            const active = settingsDialog.querySelector(":focus") || (settingsDialog.contains(document2.activeElement) ? document2.activeElement : null), focusId = active?.id || "", selection = active && Number.isInteger(active.selectionStart) ? [active.selectionStart, active.selectionEnd] : null, sidebar = settingsDialog.querySelector(".settings-sidebar");
            if (sidebar) sidebar.setAttribute("aria-label", tr("settings.navigation", "\u8BBE\u7F6E\u5BFC\u822A"));
            const title = settingsDialog.querySelector('[data-settings-copy="title"]');
            if (title) title.textContent = tr("settings.title", "\u8BBE\u7F6E");
            const copies = [["activation", "settings.activationTitle", "\u6C38\u4E45\u6FC0\u6D3B"], ["web-dock", "settings.dockTitle", "\u7F51\u9875\u62D3\u5C55\u575E"], ["ai", "ai.settings.nav", "AI \u667A\u80FD\u6574\u7406"], ["privacy-data", "privacy.title", "\u9690\u79C1\u4E0E\u6570\u636E"]];
            copies.forEach(([section, key, fallback]) => {
              const label = settingsDialog.querySelector(`[data-settings-label="${section}"]`);
              if (label) label.textContent = tr(key, fallback);
            });
            settingsDialog.querySelector("#settingsDialogClose")?.setAttribute("aria-label", tr("settings.close", "\u5173\u95ED\u8BBE\u7F6E"));
            selectSettingsSection(settingsSection);
            activationRuntime.refreshLanguage?.();
            const next = focusId && settingsDialog.querySelector(`#${focusId}`);
            next?.focus?.({ preventScroll: true });
            if (selection && next?.setSelectionRange) next.setSelectionRange(...selection);
            return true;
          }
          function containsEvent(event) {
            if (!settingsDialog || !event) return false;
            const path = typeof event.composedPath === "function" ? event.composedPath() : [];
            return path.includes(settingsDialog) || Boolean(event.target && settingsDialog.contains(event.target));
          }
          function destroy() {
            if (destroyed) return;
            destroyed = true;
            pageWindow?.removeEventListener?.("pagehide", handlePagehide);
            cancelOpenEffects();
            closeSettingsDialog(false, true);
            backupSection.destroy();
          }
          function setSettingsError(message) {
            dockSettingsError = String(message || "");
          }
          return {
            open: openSettingsDialog,
            close: closeSettingsDialog,
            forceClose,
            selectSection: selectSettingsSection,
            refreshLanguage,
            render: renderWebDockSettings,
            syncControls: syncWebDockSettingsControls,
            showError: showDockControlError,
            setSettingsError,
            getDialog,
            getSection,
            isOpen,
            containsEvent,
            backupSection,
            destroy
          };
        }
        return { createDockSettingsView };
      });
    }
  });

  // src/shared/site-editor-view.js
  var require_site_editor_view = __commonJS({
    "src/shared/site-editor-view.js"(exports, module) {
      (function attachSiteEditorView(root, factory) {
        const api = factory(root.TfaDockLogic || (typeof __require === "function" ? require_dock_logic() : null));
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaSiteEditorView = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createSiteEditorViewModule(DockLogic) {
        "use strict";
        const FIELD_SCHEMA = Object.freeze([
          Object.freeze({ key: "title", type: "text", maxLength: 120, required: true }),
          Object.freeze({ key: "url", type: "url", required: true }),
          Object.freeze({ key: "categoryId", type: "select", required: false }),
          Object.freeze({ key: "iconSource", type: "choice", values: Object.freeze(["auto", "text", "upload"]) })
        ]);
        const TEXT_ICON_COLORS = DockLogic?.DOCK_TEXT_ICON_COLORS || Object.freeze([
          Object.freeze({ id: "violet", hex: "#7568dc" })
        ]);
        const DEFAULT_COPY = Object.freeze({
          category: "Category",
          title: "Site title",
          url: "Web address",
          icon: "Site icon",
          iconSource: "Icon source",
          iconAuto: "Fetch automatically",
          iconText: "Text icon",
          iconUpload: "Local upload",
          iconTextLabel: "Icon text",
          iconTextPlaceholder: "Up to 10 letters",
          iconColor: "Text icon color",
          uploadChoose: "Choose a local image",
          uploadHint: "PNG / JPEG / WebP / SVG, up to 512 KB"
        });
        function safeUrl(value) {
          const input = String(value || "").trim();
          if (!input) return "";
          const explicitScheme = /^[a-z][a-z\d+.-]*:/i.test(input);
          const hasHttpScheme = /^https?:\/\//i.test(input);
          if (explicitScheme && !hasHttpScheme) {
            const bareHostPort = /^(?:localhost|(?:[a-z\d-]+\.)+[a-z\d-]+|\[[\da-f:]+\]):\d+(?:[/?#]|$)/i.test(input);
            if (!bareHostPort) return "";
          }
          const candidate = hasHttpScheme ? input : `https://${input.replace(/^\/\//, "")}`;
          try {
            const url = new URL(candidate);
            if (!["http:", "https:"].includes(url.protocol)) return "";
            const hostname = url.hostname;
            if (!hostname || !hostname.includes(".") && hostname !== "localhost" && !/^\[[\da-f:]+\]$/i.test(hostname)) return "";
            url.hash = "";
            return url.href;
          } catch (_error) {
            return "";
          }
        }
        function validateSiteInput(value = {}) {
          const title = String(value.title || "").trim().slice(0, 120);
          const url = safeUrl(value.url);
          if (!title) return { ok: false, field: "title", error: "title-required" };
          if (!url) return { ok: false, field: "url", error: "url-invalid" };
          return { ok: true, value: { title, url } };
        }
        function createSiteEditorView(options = {}) {
          const escapeHtml = options.escapeHtml || ((value) => String(value || "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;"));
          const colors = Array.isArray(options.textIconColors) && options.textIconColors.length ? options.textIconColors : TEXT_ICON_COLORS;
          function buildColorButtons(selectedColor, attributes = "") {
            const selected = String(selectedColor || "").toLowerCase();
            return colors.map((color, index) => {
              const active = selected ? color.hex === selected || color.id === selected : index === 0;
              return `<button type="button" class="dock-add-text-color color-swatch${active ? " active is-selected" : ""}" data-site-editor-color="${escapeHtml(color.id)}" data-site-editor-color-hex="${escapeHtml(color.hex)}" data-dock-add-icon-color="${escapeHtml(color.hex)}" aria-label="${escapeHtml(color.id)}" aria-pressed="${active}" style="--dock-add-text-color:${escapeHtml(color.hex)};background:${escapeHtml(color.hex)}" ${attributes}></button>`;
            }).join("");
          }
          function buildFieldsMarkup(config = {}) {
            const copy = { ...DEFAULT_COPY, ...config.copy || {} };
            const ids = {
              title: "dockAddTitle",
              url: "dockAddUrl",
              category: "siteCategoryInput",
              iconText: "dockAddIconText",
              iconTextCount: "dockAddIconTextCount",
              iconFile: "dockAddIconFile",
              uploadName: "dockAddUploadName",
              colorPicker: "dockAddTextColors",
              ...config.ids || {}
            };
            const editSite = config.editSite || null;
            const standard = config.standardAttribute === false ? "" : " data-dock-add-standard-field";
            const categories = Array.isArray(config.categories) ? config.categories : [];
            const categoryMarkup = categories.length ? `<label for="${escapeHtml(ids.category)}" class="site-editor-field"${standard}>${escapeHtml(copy.category)}</label>
        <select id="${escapeHtml(ids.category)}" class="site-editor-control"${standard}>${categories.map((category) => `<option value="${escapeHtml(category.id)}"${category.id === config.selectedCategoryId ? " selected" : ""}>${escapeHtml(category.title)}</option>`).join("")}</select>` : "";
            const allowUpload = config.allowUpload !== false;
            return `<label for="${escapeHtml(ids.title)}" class="site-editor-field"${standard}>${escapeHtml(copy.title)}</label>
        <input id="${escapeHtml(ids.title)}" class="site-editor-control"${standard} type="text" maxlength="120" autocomplete="off" value="${escapeHtml(editSite?.title || "")}">
        <label for="${escapeHtml(ids.url)}" class="site-editor-field"${standard}>${escapeHtml(copy.url)}</label>
        <input id="${escapeHtml(ids.url)}" class="site-editor-control"${standard} type="text" inputmode="url" autocomplete="url" value="${escapeHtml(editSite?.url || "")}">
        ${categoryMarkup}
        <fieldset class="dock-add-icon-picker site-editor-icon-picker"${standard} data-dock-add-icon-field>
          <legend>${escapeHtml(copy.icon)}</legend>
          <div class="dock-add-icon-tabs site-editor-icon-tabs" role="group" aria-label="${escapeHtml(copy.iconSource)}">
            <button type="button" data-site-editor-icon-source="auto" data-dock-add-icon-source="auto" aria-pressed="true">${escapeHtml(copy.iconAuto)}</button>
            <button type="button" data-site-editor-icon-source="text" data-dock-add-icon-source="text" aria-pressed="false">${escapeHtml(copy.iconText)}</button>
            ${allowUpload ? `<button type="button" data-site-editor-icon-source="upload" data-dock-add-icon-source="upload" aria-pressed="false">${escapeHtml(copy.iconUpload)}</button>` : ""}
          </div>
          <div class="dock-add-icon-field dock-add-text-icon-field site-editor-text-field" data-dock-add-text-icon hidden>
            <label for="${escapeHtml(ids.iconText)}">${escapeHtml(copy.iconTextLabel)} <span id="${escapeHtml(ids.iconTextCount)}">0 / 10</span></label>
            <input id="${escapeHtml(ids.iconText)}" type="text" maxlength="10" autocomplete="off" placeholder="${escapeHtml(copy.iconTextPlaceholder)}">
            <div class="dock-add-text-colors color-picker" id="${escapeHtml(ids.colorPicker)}" role="group" aria-label="${escapeHtml(copy.iconColor)}">${buildColorButtons(editSite?.iconColor)}</div>
          </div>
          ${allowUpload ? `<div class="dock-add-upload-field" data-dock-add-upload-icon hidden>
            <label class="dock-add-upload-button" for="${escapeHtml(ids.iconFile)}">${escapeHtml(copy.uploadChoose)}</label>
            <input class="sr-only" id="${escapeHtml(ids.iconFile)}" type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml">
            <span id="${escapeHtml(ids.uploadName)}">${escapeHtml(copy.uploadHint)}</span>
          </div>` : ""}
        </fieldset>`;
          }
          return Object.freeze({ buildColorButtons, buildFieldsMarkup });
        }
        return Object.freeze({ FIELD_SCHEMA, TEXT_ICON_COLORS, DEFAULT_COPY, safeUrl, validateSiteInput, createSiteEditorView });
      });
    }
  });

  // src/content/dock-add-dialog-view.js
  var require_dock_add_dialog_view = __commonJS({
    "src/content/dock-add-dialog-view.js"(exports, module) {
      (function(root, factory) {
        const api = factory(root.TfaSiteEditorView || (typeof __require === "function" ? require_site_editor_view() : null));
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockAddDialogView = api;
        else if (!commonJs) root.TfaDockAddDialogView = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function(SiteEditorView) {
        "use strict";
        function createDockAddDialogView({
          chrome: chrome2,
          escapeHtml,
          WebDockCatalog,
          textIconColors = [],
          translate = () => ""
        }) {
          const siteEditor = SiteEditorView.createSiteEditorView({ escapeHtml, textIconColors });
          const tr = (key, fallback, params = {}) => {
            const translated = translate(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          function buildCatalogMarkup() {
            if (!WebDockCatalog || !Array.isArray(WebDockCatalog.sites)) return "";
            const categories = Array.from(new Set(WebDockCatalog.sites.map((site) => site.category)));
            const defaultCategory = "all";
            const allLabel = tr("filter.all", "\u5168\u90E8");
            const categoryMarkup = [allLabel, ...categories].map((category) => {
              const value = category === allLabel ? "all" : category;
              const count = value === "all" ? WebDockCatalog.sites.length : WebDockCatalog.sites.filter((site) => site.category === value).length;
              const selected = value === defaultCategory;
              return `<button type="button" role="tab" class="dock-add-catalog-category${selected ? " active" : ""}" data-dock-catalog-category="${escapeHtml(value)}" aria-selected="${selected}"><span>${escapeHtml(category)}</span><em>${count}</em></button>`;
            }).join("");
            const items = WebDockCatalog.sites.map((site) => `<button type="button" class="dock-add-catalog-item" data-dock-catalog-id="${escapeHtml(site.id)}" data-dock-catalog-category-value="${escapeHtml(site.category)}" data-dock-catalog-search="${escapeHtml(`${site.title} ${site.category}`.toLowerCase())}" title="${escapeHtml(site.title)}" ${defaultCategory === "all" || site.category === defaultCategory ? "" : "hidden"}><span class="dock-add-catalog-icon"><img src="${escapeHtml(chrome2.runtime.getURL(site.iconFile))}" alt="" draggable="false"><b hidden>${escapeHtml(Array.from(site.title.trim())[0] || "?")}</b></span><span>${escapeHtml(site.title)}</span></button>`).join("");
            return `<section class="dock-add-catalog">
        <div class="dock-add-catalog-heading"><span>${escapeHtml(tr("dock.add.catalogQuick", "\u5FEB\u6377\u9009\u62E9\u5E38\u7528\u7F51\u7AD9"))}</span><strong>${escapeHtml(tr("dock.add.siteCount", "{count} \u4E2A", { count: WebDockCatalog.sites.length }))}</strong></div>
        <div class="dock-add-catalog-tools"><input id="dockAddCatalogSearch" type="search" placeholder="${escapeHtml(tr("dock.add.catalogSearchPlaceholder", "\u641C\u7D22\u7F51\u7AD9\u6216\u5206\u7C7B"))}" autocomplete="off"></div>
        <div class="dock-add-catalog-categories" id="dockAddCatalogCategories" role="tablist" aria-label="${escapeHtml(tr("dock.add.catalogCategories", "\u5E38\u7528\u7F51\u7AD9\u5206\u7C7B"))}">${categoryMarkup}</div>
        <div class="dock-add-catalog-list" id="dockAddCatalogList">${items}</div>
      </section>`;
          }
          function buildDialogMarkup(editSite, catalogMarkup) {
            return `
        <div class="dock-add-overlay" data-dock-add-dismiss></div>
        <section class="dock-add-dialog" role="dialog" aria-modal="true" aria-labelledby="dockAddTitleHeading">
          <header class="dock-add-header">
            <div><span>WEB DOCK</span><h2 id="dockAddTitleHeading">${escapeHtml(editSite ? tr("dock.add.editTitle", "\u7F16\u8F91\u7F51\u7AD9") : tr("dock.add.addTitle", "\u65B0\u589E\u7F51\u7AD9"))}</h2><p>${escapeHtml(editSite ? tr("dock.add.editDescription", "\u4FEE\u6539\u7F51\u7AD9\u6807\u9898\u3001\u7F51\u5740\u548C\u56FE\u6807") : tr("dock.add.addDescription", "\u4FDD\u5B58\u5F53\u524D\u9875\u9762\uFF0C\u6216\u6DFB\u52A0\u4EFB\u610F\u5E38\u7528\u7F51\u7AD9"))}</p></div>
            <button class="dock-add-close" id="dockAddClose" type="button" aria-label="${escapeHtml(tr("dock.add.close", "\u5173\u95ED\u65B0\u589E\u7F51\u7AD9\u5F39\u7A97"))}">\xD7</button>
          </header>
          <div class="dock-add-mode-switch" role="group" aria-label="${escapeHtml(tr("dock.add.modeLabel", "\u65B0\u589E\u65B9\u5F0F"))}">
            <button class="active" type="button" data-dock-add-mode="current" aria-pressed="true">${escapeHtml(tr("dock.add.mode.current", "\u4FDD\u5B58\u5F53\u524D\u7F51\u9875"))}</button>
            <button type="button" data-dock-add-mode="custom" aria-pressed="false">${escapeHtml(tr("dock.add.mode.custom", "\u81EA\u5B9A\u4E49\u65B0\u589E"))}</button>
            <button type="button" data-dock-add-mode="catalog" aria-pressed="false">${escapeHtml(tr("dock.add.mode.catalog", "\u9009\u62E9\u5E38\u7528\u7F51\u7AD9"))}</button>
            <button type="button" data-dock-add-mode="bookmarks" aria-pressed="false">${escapeHtml(tr("dock.add.mode.bookmarks", "\u4ECE\u4E66\u7B7E\u5BFC\u5165"))}</button>
          </div>
          <form class="dock-add-form" id="dockAddForm" novalidate>
            <div class="dock-add-scroll-area">
              <div class="dock-add-fields">
              ${catalogMarkup}
              <section class="dock-add-bookmarks" id="dockAddBookmarks" hidden>
                <div class="dock-add-bookmark-heading"><strong>${escapeHtml(tr("dock.add.bookmarksHeading", "\u9009\u62E9 Chrome \u4E66\u7B7E"))}</strong><span id="dockAddBookmarkCount">${escapeHtml(tr("dock.add.bookmarkCount", "{count} \u4E2A", { count: 0 }))}</span></div>
                <input id="dockAddBookmarkSearch" type="search" placeholder="${escapeHtml(tr("dock.add.bookmarkSearchPlaceholder", "\u641C\u7D22\u6807\u9898\u3001\u7F51\u5740\u6216\u6587\u4EF6\u5939"))}" autocomplete="off">
                <div class="dock-add-bookmark-state" id="dockAddBookmarkState" role="status">${escapeHtml(tr("dock.add.bookmarksLoading", "\u6B63\u5728\u8BFB\u53D6 Chrome \u4E66\u7B7E\u2026"))}</div>
                <button class="dock-add-bookmark-permission" id="dockAddBookmarkPermission" type="button" hidden>${escapeHtml(tr("permission.bookmarks.allow", "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E"))}</button>
                <div class="dock-add-bookmark-list" id="dockAddBookmarkList" aria-label="${escapeHtml(tr("dock.add.bookmarksList", "Chrome \u4E66\u7B7E\u5217\u8868"))}"></div>
              </section>
              ${siteEditor.buildFieldsMarkup({ editSite, allowUpload: true, copy: {
              category: tr("siteEditor.category", "\u5206\u7C7B"),
              title: tr("siteEditor.title", "\u7F51\u7AD9\u6807\u9898"),
              url: tr("siteEditor.url", "\u7F51\u9875\u5730\u5740"),
              icon: tr("siteEditor.icon", "\u7F51\u7AD9\u56FE\u6807"),
              iconSource: tr("siteEditor.iconSource", "\u56FE\u6807\u6765\u6E90"),
              iconAuto: tr("siteEditor.iconAuto", "\u81EA\u52A8\u83B7\u53D6"),
              iconText: tr("siteEditor.iconText", "\u6587\u5B57\u56FE\u6807"),
              iconUpload: tr("siteEditor.iconUpload", "\u672C\u5730\u4E0A\u4F20"),
              iconTextLabel: tr("siteEditor.iconTextLabel", "\u56FE\u6807\u6587\u5B57"),
              iconTextPlaceholder: tr("siteEditor.iconTextPlaceholder", "\u6700\u591A 10 \u4E2A\u5B57\u7B26"),
              iconColor: tr("siteEditor.iconColor", "\u6587\u5B57\u56FE\u6807\u989C\u8272"),
              uploadChoose: tr("siteEditor.uploadChoose", "\u9009\u62E9\u672C\u5730\u56FE\u7247"),
              uploadHint: tr("siteEditor.uploadHint", "PNG / JPEG / WebP / SVG\uFF0C\u4E0D\u8D85\u8FC7 512 KB")
            } })}
              </div>
              <aside class="dock-add-preview-panel" aria-label="${escapeHtml(tr("dock.add.previewAria", "\u56FE\u6807\u5B9E\u65F6\u9884\u89C8"))}">
                <span class="dock-add-preview-caption">${escapeHtml(tr("dock.add.previewCaption", "\u5B9E\u65F6\u9884\u89C8"))}</span>
                <div class="dock-add-preview-card">
                  <span class="dock-add-preview-icon"><span id="dockAddPreviewFallback">?</span><img id="dockAddPreviewImage" alt="" draggable="false" referrerpolicy="no-referrer"></span>
                  <strong id="dockAddPreviewTitle">${escapeHtml(tr("dock.add.previewTitle", "\u7F51\u7AD9\u6807\u9898"))}</strong>
                  <span id="dockAddPreviewUrl">https://example.com/</span>
                </div>
                <p>${escapeHtml(tr("dock.add.previewHint", "\u81EA\u52A8\u56FE\u6807\u4E0D\u53EF\u7528\u65F6\uFF0C\u4F1A\u4F9D\u6B21\u964D\u7EA7\u5230\u6B21\u6E05\u56FE\u6807\u4E0E\u6587\u5B57\u3002"))}</p>
              </aside>
              <div class="dock-add-error" id="dockAddError" role="alert" hidden></div>
            </div>
            <footer class="dock-add-actions">
              <button type="button" data-dock-add-cancel>${escapeHtml(tr("dock.add.cancel", "\u53D6\u6D88"))}</button>
              <button class="dock-add-submit" id="dockAddSubmit" type="submit">${escapeHtml(tr("dock.add.save", "\u4FDD\u5B58\u5230\u62D3\u5C55\u575E"))}</button>
            </footer>
          </form>
        </section>`;
          }
          function localizeDialog(dialog, editSite = null) {
            if (!dialog?.querySelector) return false;
            const text = (selector, key, fallback) => {
              const node = dialog.querySelector(selector);
              if (node) node.textContent = tr(key, fallback);
            };
            const attribute = (selector, name, key, fallback) => {
              dialog.querySelector(selector)?.setAttribute?.(name, tr(key, fallback));
            };
            text("#dockAddTitleHeading", editSite ? "dock.add.editTitle" : "dock.add.addTitle", editSite ? "\u7F16\u8F91\u7F51\u7AD9" : "\u65B0\u589E\u7F51\u7AD9");
            text(
              ".dock-add-header p",
              editSite ? "dock.add.editDescription" : "dock.add.addDescription",
              editSite ? "\u4FEE\u6539\u7F51\u7AD9\u6807\u9898\u3001\u7F51\u5740\u548C\u56FE\u6807" : "\u4FDD\u5B58\u5F53\u524D\u9875\u9762\uFF0C\u6216\u6DFB\u52A0\u4EFB\u610F\u5E38\u7528\u7F51\u7AD9"
            );
            attribute("#dockAddClose", "aria-label", "dock.add.close", "\u5173\u95ED\u65B0\u589E\u7F51\u7AD9\u5F39\u7A97");
            attribute(".dock-add-mode-switch", "aria-label", "dock.add.modeLabel", "\u65B0\u589E\u65B9\u5F0F");
            for (const [mode, fallback] of Object.entries({
              current: "\u4FDD\u5B58\u5F53\u524D\u7F51\u9875",
              custom: "\u81EA\u5B9A\u4E49\u65B0\u589E",
              catalog: "\u9009\u62E9\u5E38\u7528\u7F51\u7AD9",
              bookmarks: "\u4ECE\u4E66\u7B7E\u5BFC\u5165"
            })) text(`[data-dock-add-mode="${mode}"]`, `dock.add.mode.${mode}`, fallback);
            text(".dock-add-bookmark-heading strong", "dock.add.bookmarksHeading", "\u9009\u62E9 Chrome \u4E66\u7B7E");
            text("#dockAddBookmarkPermission", "permission.bookmarks.allow", "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E");
            attribute("#dockAddBookmarkSearch", "placeholder", "dock.add.bookmarkSearchPlaceholder", "\u641C\u7D22\u6807\u9898\u3001\u7F51\u5740\u6216\u6587\u4EF6\u5939");
            attribute("#dockAddBookmarkList", "aria-label", "dock.add.bookmarksList", "Chrome \u4E66\u7B7E\u5217\u8868");
            attribute(".dock-add-preview-panel", "aria-label", "dock.add.previewAria", "\u56FE\u6807\u5B9E\u65F6\u9884\u89C8");
            text(".dock-add-preview-caption", "dock.add.previewCaption", "\u5B9E\u65F6\u9884\u89C8");
            text(".dock-add-preview-panel p", "dock.add.previewHint", "\u81EA\u52A8\u56FE\u6807\u4E0D\u53EF\u7528\u65F6\uFF0C\u4F1A\u4F9D\u6B21\u964D\u7EA7\u5230\u6B21\u6E05\u56FE\u6807\u4E0E\u6587\u5B57\u3002");
            text("[data-dock-add-cancel]", "dock.add.cancel", "\u53D6\u6D88");
            const fieldCopy = [
              ['label[for="dockAddTitle"]', "siteEditor.title", "\u7F51\u7AD9\u6807\u9898"],
              ['label[for="dockAddUrl"]', "siteEditor.url", "\u7F51\u9875\u5730\u5740"],
              [".site-editor-icon-picker legend", "siteEditor.icon", "\u7F51\u7AD9\u56FE\u6807"],
              ['[data-site-editor-icon-source="auto"]', "siteEditor.iconAuto", "\u81EA\u52A8\u83B7\u53D6"],
              ['[data-site-editor-icon-source="text"]', "siteEditor.iconText", "\u6587\u5B57\u56FE\u6807"],
              ['[data-site-editor-icon-source="upload"]', "siteEditor.iconUpload", "\u672C\u5730\u4E0A\u4F20"],
              [".dock-add-upload-button", "siteEditor.uploadChoose", "\u9009\u62E9\u672C\u5730\u56FE\u7247"]
            ];
            fieldCopy.forEach(([selector, key, fallback]) => text(selector, key, fallback));
            attribute(".site-editor-icon-tabs", "aria-label", "siteEditor.iconSource", "\u56FE\u6807\u6765\u6E90");
            attribute("#dockAddIconText", "placeholder", "siteEditor.iconTextPlaceholder", "\u6700\u591A 10 \u4E2A\u5B57\u7B26");
            attribute("#dockAddTextColors", "aria-label", "siteEditor.iconColor", "\u6587\u5B57\u56FE\u6807\u989C\u8272");
            return true;
          }
          return { buildCatalogMarkup, buildDialogMarkup, localizeDialog };
        }
        return { createDockAddDialogView };
      });
    }
  });

  // src/content/dock-add-dialog.js
  var require_dock_add_dialog = __commonJS({
    "src/content/dock-add-dialog.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockAddDialog = api;
        else if (!commonJs) root.TfaDockAddDialog = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockAddDialog(deps) {
          const {
            chrome: chrome2,
            document: document2,
            FileReader,
            Image,
            location,
            WebDockCatalog,
            view,
            context,
            closeOthers,
            syncHost,
            setExpanded,
            send,
            showToast,
            escapeHtml,
            hostOf,
            domainColor,
            letterOf,
            serializedByteLength,
            normalizeDockUrl,
            normalizeDockIconText,
            dockResponseMessage,
            builtInWebDockIcon,
            chromeDockFaviconUrl,
            normalizeDockTextIconColor,
            textIconColors,
            translate = () => "",
            collectPageIconCandidates,
            persistWebDockSite,
            setWebDockCustomIcon,
            removeWebDockCustomIcon,
            getWebDockCustomIcon,
            requestFrame = requestAnimationFrame,
            cancelFrame = typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : () => {
            },
            uploadMaxBytes = 512 * 1024,
            customIconMaxLength = 60 * 1024
          } = deps;
          const WEB_DOCK_UPLOAD_MAX_BYTES = uploadMaxBytes;
          const WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH = customIconMaxLength;
          const dockFaviconRuntime = { getCustomIcon: getWebDockCustomIcon };
          const tr = (key, fallback, params = {}) => {
            const translated = translate(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          const dialogView = view.createDockAddDialogView({
            chrome: chrome2,
            escapeHtml,
            WebDockCatalog,
            textIconColors,
            translate
          });
          const { buildCatalogMarkup, buildDialogMarkup } = dialogView;
          let dockAddDialog = null;
          let dockAddMode = "current";
          let dockAddSubmitPending = false;
          let dockAddRestoreFocus = null;
          let dockAddUploadedIcon = "";
          let dockAddEditingSiteId = "";
          let dockAddBookmarks = [];
          let dockAddBookmarksLoading = false, dockAddBookmarksError = "";
          let dockAddBookmarksPermission = null, dockAddBookmarksPermissionPending = false;
          let dockAddBookmarkRequestSequence = 0;
          let dockAddSelectedBookmarkUrls = /* @__PURE__ */ new Set();
          let destroyed = false;
          let entryFrame = null;
          let lifecycle = 0;
          function readFileAsDataUrl(file) {
            return new Promise((resolve, reject) => {
              const reader = new FileReader();
              reader.addEventListener("load", () => resolve(String(reader.result || "")));
              reader.addEventListener("error", () => reject(new Error(tr("dock.add.imageReadingFailed", "\u8BFB\u53D6\u56FE\u7247\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"))));
              reader.readAsDataURL(file);
            });
          }
          function loadDockAddImage(source) {
            return new Promise((resolve, reject) => {
              const image = new Image();
              image.addEventListener("load", () => resolve(image), { once: true });
              image.addEventListener("error", () => reject(new Error(tr("dock.add.imageUnrecognized", "\u65E0\u6CD5\u8BC6\u522B\u8FD9\u5F20\u56FE\u7247"))), { once: true });
              image.src = source;
            });
          }
          async function compressDockAddIcon(file) {
            const allowedTypes = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp", "image/svg+xml"]);
            if (!file || !allowedTypes.has(file.type)) throw new Error(tr("dock.add.imageTypeInvalid", "\u4EC5\u652F\u6301 PNG\u3001JPEG\u3001WebP \u6216 SVG \u56FE\u7247"));
            if (file.size > WEB_DOCK_UPLOAD_MAX_BYTES) throw new Error(tr("dock.add.imageTooLarge", "\u56FE\u7247\u4E0D\u80FD\u8D85\u8FC7 512KB"));
            const source = await readFileAsDataUrl(file);
            const image = await loadDockAddImage(source);
            const naturalWidth = Number(image.naturalWidth) || 0;
            const naturalHeight = Number(image.naturalHeight) || 0;
            if (!naturalWidth || !naturalHeight) throw new Error(tr("dock.add.imageSizeInvalid", "\u56FE\u7247\u5C3A\u5BF8\u65E0\u6548"));
            const initialScale = Math.min(1, 256 / Math.max(naturalWidth, naturalHeight));
            const canvas = document2.createElement("canvas");
            const context2 = canvas.getContext("2d", { alpha: true });
            if (!context2) throw new Error(tr("dock.add.imageUnavailable", "\u5F53\u524D\u9875\u9762\u65E0\u6CD5\u5904\u7406\u56FE\u7247"));
            const dimensionScales = [1, 0.8, 0.64, 0.5];
            const qualities = [0.88, 0.76, 0.64, 0.52];
            let smallest = "";
            for (const dimensionScale of dimensionScales) {
              canvas.width = Math.max(1, Math.round(naturalWidth * initialScale * dimensionScale));
              canvas.height = Math.max(1, Math.round(naturalHeight * initialScale * dimensionScale));
              context2.clearRect(0, 0, canvas.width, canvas.height);
              context2.drawImage(image, 0, 0, canvas.width, canvas.height);
              for (const quality of qualities) {
                const encoded = canvas.toDataURL("image/webp", quality);
                if (!smallest || encoded.length < smallest.length) smallest = encoded;
                if (encoded.length <= WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH) return encoded;
              }
            }
            if (smallest && smallest.length <= WEB_DOCK_CUSTOM_ICON_MAX_DATA_URL_LENGTH) return smallest;
            throw new Error(tr("dock.add.imageCompressedTooLarge", "\u56FE\u7247\u538B\u7F29\u540E\u4ECD\u7136\u8FC7\u5927\uFF0C\u8BF7\u6362\u4E00\u5F20\u66F4\u7B80\u6D01\u7684\u56FE\u7247"));
          }
          function dockAddFormValue(selector) {
            const input = dockAddDialog && dockAddDialog.querySelector(selector);
            return input ? input.value.trim() : "";
          }
          function setDockAddError(message = "") {
            if (!dockAddDialog) return;
            const error = dockAddDialog.querySelector("#dockAddError");
            if (!error) return;
            error.textContent = message;
            error.hidden = !message;
          }
          function selectedDockAddIconSource() {
            const selected = dockAddDialog && dockAddDialog.querySelector('[data-dock-add-icon-source][aria-pressed="true"]');
            return selected ? selected.dataset.dockAddIconSource : "auto";
          }
          function selectedDockAddIconColor() {
            const selected = dockAddDialog?.querySelector('[data-dock-add-icon-color][aria-pressed="true"]');
            return normalizeDockTextIconColor?.(selected?.dataset?.dockAddIconColor, textIconColors?.[0]?.hex) || textIconColors?.[0]?.hex || "";
          }
          function syncDockAddPreview() {
            if (!dockAddDialog) return;
            const title = dockAddFormValue("#dockAddTitle");
            const rawUrl = dockAddFormValue("#dockAddUrl");
            let normalizedUrl = "";
            try {
              normalizedUrl = normalizeDockUrl(rawUrl);
            } catch (_error) {
            }
            const sourceMode = selectedDockAddIconSource();
            let source = "";
            let iconText = "";
            if (sourceMode === "upload") source = dockAddUploadedIcon;
            else if (sourceMode === "text") iconText = dockAddFormValue("#dockAddIconText");
            else if (normalizedUrl) {
              if (dockAddMode === "current" && normalizedUrl === normalizeDockUrl(location.href)) {
                const pageIcon = collectPageIconCandidates()[0];
                source = pageIcon ? pageIcon.url : chromeDockFaviconUrl({ url: normalizedUrl });
              } else {
                source = dockFaviconRuntime.getCustomIcon(normalizedUrl) || builtInWebDockIcon({ url: normalizedUrl }) || chromeDockFaviconUrl({ url: normalizedUrl });
              }
            }
            const image = dockAddDialog.querySelector("#dockAddPreviewImage");
            const fallback = dockAddDialog.querySelector("#dockAddPreviewFallback");
            const titleLabel = dockAddDialog.querySelector("#dockAddPreviewTitle");
            const urlLabel = dockAddDialog.querySelector("#dockAddPreviewUrl");
            const fallbackText = iconText || (hostOf(normalizedUrl) || title || "?").charAt(0).toUpperCase() || "?";
            fallback.textContent = fallbackText;
            fallback.dataset.iconTextLength = String(iconText.length);
            fallback.style.background = sourceMode === "text" ? selectedDockAddIconColor() : domainColor(normalizedUrl || rawUrl);
            titleLabel.textContent = title || tr("dock.add.previewTitle", "\u7F51\u7AD9\u6807\u9898");
            urlLabel.textContent = normalizedUrl || rawUrl || "https://example.com/";
            const showFallback = () => {
              image.hidden = true;
              fallback.hidden = false;
            };
            if (!source) {
              image.removeAttribute("src");
              showFallback();
              return;
            }
            image.onload = () => {
              image.hidden = false;
              fallback.hidden = true;
            };
            image.onerror = showFallback;
            image.hidden = false;
            fallback.hidden = true;
            image.src = source;
          }
          function setDockAddIconSource(source) {
            if (!dockAddDialog || dockAddSubmitPending) return;
            const nextSource = ["auto", "text", "upload"].includes(source) ? source : "auto";
            dockAddDialog.querySelectorAll("[data-dock-add-icon-source]").forEach((button) => {
              button.setAttribute("aria-pressed", String(button.dataset.dockAddIconSource === nextSource));
            });
            const textField = dockAddDialog.querySelector("[data-dock-add-text-icon]");
            const uploadField = dockAddDialog.querySelector("[data-dock-add-upload-icon]");
            if (textField) textField.hidden = nextSource !== "text";
            if (uploadField) uploadField.hidden = nextSource !== "upload";
            setDockAddError("");
            syncDockAddPreview();
          }
          function normalizeDockAddBookmark(value) {
            if (!value || typeof value !== "object" || !value.url) return null;
            let url = "";
            try {
              url = normalizeDockUrl(value.url);
            } catch (_error) {
              return null;
            }
            const title = String(value.title || hostOf(url) || tr("dock.add.untitledBookmark", "\u672A\u547D\u540D\u4E66\u7B7E")).trim() || tr("dock.add.untitledBookmark", "\u672A\u547D\u540D\u4E66\u7B7E");
            return {
              id: String(value.id || url),
              title,
              url,
              path: String(value.path || tr("dock.add.otherBookmarks", "\u5176\u4ED6\u4E66\u7B7E")).trim() || tr("dock.add.otherBookmarks", "\u5176\u4ED6\u4E66\u7B7E")
            };
          }
          function renderDockAddBookmarks() {
            if (!dockAddDialog) return;
            const state = dockAddDialog.querySelector("#dockAddBookmarkState");
            const list = dockAddDialog.querySelector("#dockAddBookmarkList");
            const count = dockAddDialog.querySelector("#dockAddBookmarkCount");
            const search = dockAddDialog.querySelector("#dockAddBookmarkSearch");
            const permissionButton = dockAddDialog.querySelector("#dockAddBookmarkPermission");
            if (!state || !list || !count) return;
            if (permissionButton) permissionButton.hidden = !dockAddBookmarksPermission || dockAddBookmarksPermission.granted === true || dockAddBookmarksPermission.recoverable === false;
            if (permissionButton) permissionButton.disabled = dockAddBookmarksPermissionPending || dockAddSubmitPending;
            const query = search ? search.value.trim().toLowerCase() : "";
            const matches = dockAddBookmarks.filter((bookmark) => !query || `${bookmark.title} ${bookmark.url} ${bookmark.path}`.toLowerCase().includes(query));
            count.textContent = dockAddSelectedBookmarkUrls.size ? tr("dock.add.bookmarkSelected", "\u5DF2\u9009 {count} \u4E2A", { count: dockAddSelectedBookmarkUrls.size }) : tr("dock.add.bookmarkCount", "{count} \u4E2A", { count: dockAddBookmarks.length });
            if (dockAddBookmarksLoading) {
              state.hidden = false;
              state.textContent = tr("dock.add.bookmarksLoading", "\u6B63\u5728\u8BFB\u53D6 Chrome \u4E66\u7B7E\u2026");
              if (permissionButton) permissionButton.hidden = true;
              list.innerHTML = "";
              return;
            }
            if (dockAddBookmarksError) {
              state.hidden = false;
              state.textContent = dockAddBookmarksError;
              list.innerHTML = "";
              return;
            }
            if (!matches.length) {
              state.hidden = false;
              state.textContent = query ? tr("dock.add.bookmarksNoMatch", "\u6CA1\u6709\u5339\u914D\u7684\u4E66\u7B7E") : tr("dock.add.bookmarksEmpty", "\u6682\u65E0\u53EF\u5BFC\u5165\u7684\u7F51\u9875\u4E66\u7B7E");
              list.innerHTML = "";
              return;
            }
            state.hidden = true;
            state.textContent = "";
            const groups = /* @__PURE__ */ new Map();
            matches.forEach((bookmark) => {
              if (!groups.has(bookmark.path)) groups.set(bookmark.path, []);
              groups.get(bookmark.path).push(bookmark);
            });
            list.innerHTML = Array.from(groups, ([path, bookmarks]) => `<section class="dock-add-bookmark-group"><h3>${escapeHtml(path)}</h3><div>` + bookmarks.map((bookmark) => {
              const selected = dockAddSelectedBookmarkUrls.has(bookmark.url);
              return `<button type="button" class="dock-add-bookmark-item${selected ? " selected" : ""}" data-dock-bookmark-url="${escapeHtml(bookmark.url)}" aria-pressed="${selected}"><span class="dock-add-bookmark-icon"><img src="${escapeHtml(builtInWebDockIcon(bookmark) || chromeDockFaviconUrl(bookmark))}" alt="" draggable="false" referrerpolicy="no-referrer"><b hidden>${escapeHtml(Array.from(bookmark.title)[0] || "?")}</b></span><span><strong>${escapeHtml(bookmark.title)}</strong><small>${escapeHtml(bookmark.url)}</small></span><i aria-hidden="true">${selected ? "\u2713" : ""}</i></button>`;
            }).join("") + "</div></section>").join("");
          }
          async function loadDockAddBookmarks() {
            if (!dockAddDialog || dockAddBookmarksLoading) return;
            const requestSequence = ++dockAddBookmarkRequestSequence;
            dockAddBookmarksLoading = true;
            dockAddBookmarksError = "";
            dockAddBookmarksPermission = null;
            renderDockAddBookmarks();
            let response;
            try {
              response = await send({ type: "DOCK_GET_BOOKMARKS" });
            } catch (error) {
              response = { error: error && error.message ? error.message : "" };
            }
            if (!dockAddDialog || requestSequence !== dockAddBookmarkRequestSequence) return;
            dockAddBookmarksLoading = false;
            if (!response || !response.ok || !Array.isArray(response.bookmarks)) {
              dockAddBookmarks = [];
              dockAddBookmarksPermission = /^BOOKMARKS_PERMISSION_(?:REQUIRED|DENIED)$/.test(String(response?.code || "")) ? response : null;
              dockAddBookmarksError = dockResponseMessage(response, tr("dock.add.bookmarksLoadFailed", "\u8BFB\u53D6\u4E66\u7B7E\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"));
              renderDockAddBookmarks();
              return;
            }
            const seenUrls = /* @__PURE__ */ new Set();
            dockAddBookmarks = response.bookmarks.map(normalizeDockAddBookmark).filter((bookmark) => {
              if (!bookmark || seenUrls.has(bookmark.url)) return false;
              seenUrls.add(bookmark.url);
              return true;
            });
            dockAddBookmarksError = "";
            dockAddBookmarksPermission = null;
            renderDockAddBookmarks();
          }
          async function requestDockAddBookmarksPermission() {
            if (!dockAddDialog || dockAddBookmarksPermissionPending) return false;
            dockAddBookmarksPermissionPending = true;
            renderDockAddBookmarks();
            let response;
            try {
              const request = send({ type: "DOCK_REQUEST_BOOKMARKS_PERMISSION" });
              response = await request;
            } catch (error) {
              response = { ok: false, granted: false, code: "BOOKMARKS_PERMISSION_DENIED", message: error?.message || "" };
            }
            if (!dockAddDialog) return false;
            dockAddBookmarksPermissionPending = false;
            dockAddBookmarksPermission = response?.granted === true ? null : response;
            if (response?.granted === true) {
              dockAddBookmarksError = "";
              await loadDockAddBookmarks();
              return true;
            }
            dockAddBookmarks = [];
            dockAddBookmarksError = dockResponseMessage(response, tr("permission.bookmarks.denied", "\u672A\u83B7\u5F97\u4E66\u7B7E\u6743\u9650\uFF1B\u672A\u5BFC\u5165\u4EFB\u4F55\u4E66\u7B7E\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002"));
            renderDockAddBookmarks();
            return false;
          }
          function setDockAddMode(mode) {
            if (!dockAddDialog || dockAddSubmitPending) return;
            dockAddMode = ["current", "custom", "catalog", "bookmarks"].includes(mode) ? mode : "current";
            dockAddDialog.querySelectorAll("[data-dock-add-mode]").forEach((button) => {
              const active = button.dataset.dockAddMode === dockAddMode;
              button.setAttribute("aria-pressed", String(active));
              button.classList.toggle("active", active);
            });
            const catalog = dockAddDialog.querySelector(".dock-add-catalog");
            if (catalog) catalog.hidden = dockAddMode !== "catalog";
            const bookmarkPanel = dockAddDialog.querySelector("#dockAddBookmarks");
            if (bookmarkPanel) bookmarkPanel.hidden = dockAddMode !== "bookmarks";
            dockAddDialog.querySelectorAll("[data-dock-add-standard-field]").forEach((element) => {
              element.hidden = dockAddMode === "bookmarks";
            });
            dockAddDialog.querySelectorAll("[data-dock-add-icon-field]").forEach((element) => {
              element.hidden = dockAddMode === "bookmarks" || dockAddMode === "catalog";
            });
            const title = dockAddDialog.querySelector("#dockAddTitle");
            const url = dockAddDialog.querySelector("#dockAddUrl");
            if (dockAddMode === "current") {
              title.value = document2.title || hostOf(location.href) || tr("dock.add.currentPage", "\u5F53\u524D\u7F51\u9875");
              url.value = location.href;
              url.readOnly = true;
              url.setAttribute("aria-readonly", "true");
            } else if (dockAddMode === "custom" || dockAddMode === "catalog") {
              title.value = "";
              url.value = "";
              url.readOnly = false;
              url.removeAttribute("aria-readonly");
            }
            dockAddUploadedIcon = "";
            const file = dockAddDialog.querySelector("#dockAddIconFile");
            if (file) file.value = "";
            setDockAddIconSource("auto");
            setDockAddError("");
            syncDockAddPreview();
            setDockAddSubmitPending(false);
            if (dockAddMode === "bookmarks") {
              loadDockAddBookmarks();
              const search = dockAddDialog.querySelector("#dockAddBookmarkSearch");
              if (search) search.focus();
            } else if (dockAddMode === "catalog") {
              dockAddDialog.querySelector("#dockAddCatalogSearch")?.focus();
            } else {
              title.focus();
            }
          }
          function setDockAddSubmitPending(pending) {
            dockAddSubmitPending = Boolean(pending);
            if (!dockAddDialog) return;
            const dialog = dockAddDialog.querySelector(".dock-add-dialog");
            if (dialog) dialog.setAttribute("aria-busy", String(dockAddSubmitPending));
            dockAddDialog.querySelectorAll("button, input").forEach((control) => {
              control.disabled = dockAddSubmitPending;
            });
            const submit = dockAddDialog.querySelector("#dockAddSubmit");
            if (submit) {
              submit.textContent = dockAddSubmitPending ? tr("dock.add.saving", "\u6B63\u5728\u4FDD\u5B58\u2026") : dockAddMode === "bookmarks" ? tr("dock.add.import", "\u5BFC\u5165\u5230\u62D3\u5C55\u575E") : tr("dock.add.save", "\u4FDD\u5B58\u5230\u62D3\u5C55\u575E");
            }
          }
          async function submitDockBookmarkImport() {
            const operationLifecycle = lifecycle;
            const selected = dockAddBookmarks.filter((bookmark) => dockAddSelectedBookmarkUrls.has(bookmark.url));
            if (!selected.length) {
              setDockAddError(tr("dock.add.selectBookmark", "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u4E66\u7B7E"));
              return;
            }
            const existingUrls = new Set(context().config.sites.map((site) => site.url));
            const pending = selected.filter((bookmark) => !existingUrls.has(bookmark.url));
            const duplicateCount = selected.length - pending.length;
            if (!pending.length) {
              const message = selected.length === 1 ? tr("dock.duplicate", "\u8BE5\u7F51\u7AD9\u5DF2\u7ECF\u5728\u7F51\u9875\u62D3\u5C55\u575E\u4E2D") : tr("dock.add.allDuplicates", "\u6240\u9009\u7684 {count} \u4E2A\u7F51\u7AD9\u5747\u5DF2\u5728\u7F51\u9875\u62D3\u5C55\u575E\u4E2D", { count: selected.length });
              setDockAddError(message);
              showToast(message, "info", 2200);
              return;
            }
            setDockAddError("");
            setDockAddSubmitPending(true);
            let importedCount = 0;
            try {
              for (const bookmark of pending) {
                const result = await persistWebDockSite({ title: bookmark.title, url: bookmark.url });
                if (destroyed || operationLifecycle !== lifecycle || !dockAddDialog) return;
                if (!result.ok) {
                  const reason = dockResponseMessage(result.response, tr("dock.add.importFailed", "\u5BFC\u5165\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"));
                  setDockAddError(importedCount ? tr("dock.add.importPartial", "\u5DF2\u5BFC\u5165 {count} \u4E2A\uFF0C\u968F\u540E\u5931\u8D25\uFF1A{reason}", { count: importedCount, reason }) : reason);
                  return;
                }
                importedCount += 1;
              }
            } finally {
              setDockAddSubmitPending(false);
            }
            closeDockAddDialog();
            const suffix = duplicateCount ? tr("dock.add.skipped", "\uFF0C\u8DF3\u8FC7 {count} \u4E2A\u5DF2\u56FA\u5B9A\u7F51\u7AD9", { count: duplicateCount }) : "";
            showToast(tr("dock.add.imported", "\u5DF2\u5BFC\u5165 {count} \u4E2A\u4E66\u7B7E{suffix}", { count: importedCount, suffix }), "success", 2200);
          }
          async function submitDockAddDialog(event) {
            event.preventDefault();
            if (!dockAddDialog || dockAddSubmitPending) return;
            if (dockAddMode === "bookmarks") {
              await submitDockBookmarkImport();
              return;
            }
            const title = dockAddFormValue("#dockAddTitle");
            const rawUrl = dockAddFormValue("#dockAddUrl");
            if (!title) {
              setDockAddError(tr("dock.add.titleRequired", "\u8BF7\u8F93\u5165\u7F51\u7AD9\u6807\u9898"));
              dockAddDialog.querySelector("#dockAddTitle").focus();
              return;
            }
            let url;
            try {
              url = normalizeDockUrl(rawUrl);
            } catch (error) {
              setDockAddError(error && error.message ? error.message : tr("dock.add.urlInvalid", "\u7F51\u9875\u7F51\u5740\u65E0\u6548"));
              dockAddDialog.querySelector("#dockAddUrl").focus();
              return;
            }
            const iconSource = selectedDockAddIconSource();
            const editingSite = dockAddEditingSiteId ? context().config.sites.find((candidate) => candidate.id === dockAddEditingSiteId) : null;
            const existing = context().config.sites.find((site2) => site2.url === url);
            const site = { ...editingSite || existing ? { id: (editingSite || existing).id } : {}, title, url };
            if (iconSource === "text") {
              const rawIconText = dockAddFormValue("#dockAddIconText");
              const iconText = normalizeDockIconText(rawIconText);
              if (!iconText) {
                const message = tr("dock.add.textIconInvalid", "\u6587\u5B57\u56FE\u6807\u4EC5\u652F\u6301 1\uFF5E10 \u4E2A\u4E2D\u82F1\u6587\u5B57\u6BCD");
                setDockAddError(message);
                showToast(tr("dock.add.saveFailedWithReason", "\u4FDD\u5B58\u5931\u8D25\uFF1A{reason}", { reason: message }), "error", 2600);
                dockAddDialog.querySelector("#dockAddIconText").focus();
                return;
              }
              site.iconText = iconText;
              site.iconMode = "text";
              site.iconColor = selectedDockAddIconColor();
            }
            if (iconSource === "upload" && !dockAddUploadedIcon) {
              setDockAddError(tr("dock.add.imageRequired", "\u8BF7\u5148\u9009\u62E9\u4E00\u5F20\u672C\u5730\u56FE\u7247"));
              return;
            }
            setDockAddError("");
            setDockAddSubmitPending(true);
            const operationLifecycle = lifecycle;
            let result;
            try {
              result = await persistWebDockSite(site, { replaceIcon: true });
              if (destroyed || operationLifecycle !== lifecycle || !dockAddDialog) return;
              if (!result.ok) {
                setDockAddError(dockResponseMessage(result.response, tr("dock.add.saveFailed", "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")));
                return;
              }
              if (iconSource === "upload") await setWebDockCustomIcon(url, dockAddUploadedIcon);
              else await removeWebDockCustomIcon(url);
              if (destroyed || operationLifecycle !== lifecycle || !dockAddDialog) return;
            } catch (error) {
              setDockAddError(dockResponseMessage(
                { error: error && error.message ? error.message : "" },
                tr("dock.add.saveFailed", "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")
              ));
              return;
            } finally {
              setDockAddSubmitPending(false);
            }
            closeDockAddDialog();
            showToast(editingSite || existing ? tr("dock.add.updated", "\u7F51\u9875\u5DF2\u66F4\u65B0") : tr("dock.add.added", "\u7F51\u9875\u5DF2\u6DFB\u52A0"), "success", 1800);
          }
          function openDockAddDialog(restoreFocusTarget = null, editSite = null) {
            if (destroyed) return false;
            context();
            if (dockAddDialog) {
              dockAddDialog.querySelector("#dockAddTitle")?.focus();
              return true;
            }
            closeOthers("add");
            lifecycle += 1;
            dockAddRestoreFocus = restoreFocusTarget || null;
            dockAddMode = "current";
            dockAddUploadedIcon = "";
            dockAddEditingSiteId = editSite && editSite.id ? editSite.id : "";
            const shell = document2.createElement("div");
            shell.className = "dock-add-shell";
            shell.innerHTML = buildDialogMarkup(editSite, buildCatalogMarkup());
            context().root.appendChild(shell);
            dockAddDialog = shell;
            syncHost();
            shell.addEventListener("wheel", (event) => event.stopPropagation(), { passive: true });
            shell.querySelector("#dockAddClose").addEventListener("click", () => closeDockAddDialog());
            shell.querySelector("[data-dock-add-dismiss]").addEventListener("click", () => closeDockAddDialog());
            shell.querySelector("[data-dock-add-cancel]").addEventListener("click", () => closeDockAddDialog());
            shell.querySelectorAll("[data-dock-add-mode]").forEach((button) => {
              button.addEventListener("click", () => setDockAddMode(button.dataset.dockAddMode));
            });
            shell.querySelectorAll("[data-dock-add-icon-source]").forEach((button) => {
              button.addEventListener("click", () => setDockAddIconSource(button.dataset.dockAddIconSource));
            });
            shell.querySelectorAll("[data-dock-add-icon-color]").forEach((button) => {
              button.addEventListener("click", () => {
                shell.querySelectorAll("[data-dock-add-icon-color]").forEach((candidate) => {
                  const active = candidate === button;
                  candidate.classList.toggle("active", active);
                  candidate.setAttribute("aria-pressed", String(active));
                });
                syncDockAddPreview();
              });
            });
            const catalogList = shell.querySelector("#dockAddCatalogList");
            if (catalogList) {
              catalogList.addEventListener("error", (event) => {
                const image = event.target.closest(".dock-add-catalog-icon img");
                if (!image) return;
                image.hidden = true;
                const fallback = image.parentElement && image.parentElement.querySelector("b");
                if (fallback) fallback.hidden = false;
              }, true);
              catalogList.addEventListener("click", (event) => {
                const button = event.target.closest("[data-dock-catalog-id]");
                if (!button || !WebDockCatalog) return;
                const site = WebDockCatalog.sites.find((candidate) => candidate.id === button.dataset.dockCatalogId);
                if (!site) return;
                shell.querySelector("#dockAddTitle").value = site.title;
                shell.querySelector("#dockAddUrl").value = site.url;
                setDockAddIconSource("auto");
                syncDockAddPreview();
              });
            }
            const catalogSearch = shell.querySelector("#dockAddCatalogSearch");
            const catalogCategories = shell.querySelector("#dockAddCatalogCategories");
            if (catalogSearch && catalogList && catalogCategories) {
              let activeCategory = catalogCategories.querySelector('[aria-selected="true"]')?.dataset.dockCatalogCategory || "all";
              const applyCatalogFilters = () => {
                const query = catalogSearch.value.trim().toLowerCase();
                catalogList.querySelectorAll("[data-dock-catalog-search]").forEach((button) => {
                  const matchesCategory = activeCategory === "all" || button.dataset.dockCatalogCategoryValue === activeCategory;
                  const matchesQuery = !query || button.dataset.dockCatalogSearch.includes(query);
                  button.hidden = !(matchesCategory && matchesQuery);
                });
              };
              catalogCategories.addEventListener("click", (event) => {
                const button = event.target.closest("[data-dock-catalog-category]");
                if (!button) return;
                activeCategory = button.dataset.dockCatalogCategory;
                catalogCategories.querySelectorAll("[data-dock-catalog-category]").forEach((categoryButton) => {
                  const selected = categoryButton === button;
                  categoryButton.classList.toggle("active", selected);
                  categoryButton.setAttribute("aria-selected", String(selected));
                });
                applyCatalogFilters();
              });
              catalogSearch.addEventListener("input", applyCatalogFilters);
            }
            const bookmarkSearch = shell.querySelector("#dockAddBookmarkSearch");
            const bookmarkList = shell.querySelector("#dockAddBookmarkList");
            const bookmarkPermission = shell.querySelector("#dockAddBookmarkPermission");
            if (bookmarkSearch) {
              bookmarkSearch.addEventListener("input", () => {
                setDockAddError("");
                renderDockAddBookmarks();
              });
            }
            if (bookmarkList) {
              bookmarkList.addEventListener("error", (event) => {
                const image = event.target.closest(".dock-add-bookmark-icon img");
                if (!image) return;
                image.hidden = true;
                const fallback = image.parentElement && image.parentElement.querySelector("b");
                if (fallback) fallback.hidden = false;
              }, true);
              bookmarkList.addEventListener("click", (event) => {
                const button = event.target.closest("[data-dock-bookmark-url]");
                if (!button || dockAddSubmitPending) return;
                const url = button.dataset.dockBookmarkUrl;
                if (dockAddSelectedBookmarkUrls.has(url)) dockAddSelectedBookmarkUrls.delete(url);
                else dockAddSelectedBookmarkUrls.add(url);
                const selectedBookmark = dockAddBookmarks.find((bookmark) => bookmark.url === url);
                if (selectedBookmark) {
                  shell.querySelector("#dockAddTitle").value = selectedBookmark.title;
                  shell.querySelector("#dockAddUrl").value = selectedBookmark.url;
                }
                setDockAddError("");
                renderDockAddBookmarks();
                syncDockAddPreview();
              });
            }
            bookmarkPermission?.addEventListener("click", () => {
              void requestDockAddBookmarksPermission();
            });
            shell.querySelectorAll("#dockAddTitle, #dockAddUrl, #dockAddIconText").forEach((input) => {
              input.addEventListener("input", () => {
                const count = shell.querySelector("#dockAddIconTextCount");
                const text = shell.querySelector("#dockAddIconText");
                if (count && text) count.textContent = `${Array.from(text.value).length} / 10`;
                setDockAddError("");
                syncDockAddPreview();
              });
            });
            shell.querySelector("#dockAddIconFile").addEventListener("change", async (event) => {
              const file = event.target.files && event.target.files[0];
              if (!file) return;
              const uploadLifecycle = lifecycle;
              const name = shell.querySelector("#dockAddUploadName");
              if (name) name.textContent = tr("dock.add.imageProcessing", "\u6B63\u5728\u5904\u7406\u56FE\u7247\u2026");
              try {
                dockAddUploadedIcon = await compressDockAddIcon(file);
                if (destroyed || uploadLifecycle !== lifecycle || dockAddDialog !== shell) return;
                if (name) name.textContent = file.name;
                setDockAddError("");
                syncDockAddPreview();
              } catch (error) {
                if (destroyed || uploadLifecycle !== lifecycle || dockAddDialog !== shell) return;
                dockAddUploadedIcon = "";
                event.target.value = "";
                if (name) name.textContent = tr("dock.add.imageUploadHint", "PNG / JPEG / WebP / SVG\uFF0C\u4E0D\u8D85\u8FC7 512KB");
                setDockAddError(error && error.message ? error.message : tr("dock.add.imageProcessFailed", "\u56FE\u7247\u5904\u7406\u5931\u8D25"));
                syncDockAddPreview();
              }
            });
            shell.querySelector("#dockAddForm").addEventListener("submit", submitDockAddDialog);
            dockAddBookmarks = [];
            dockAddBookmarksLoading = false;
            dockAddBookmarksError = "";
            dockAddBookmarksPermission = null;
            dockAddBookmarksPermissionPending = false;
            dockAddSelectedBookmarkUrls = /* @__PURE__ */ new Set();
            if (editSite) {
              setDockAddMode("custom");
              shell.querySelector("#dockAddTitle").value = editSite.title || "";
              shell.querySelector("#dockAddUrl").value = editSite.url || "";
              if (editSite.iconMode === "text" && editSite.iconText) {
                shell.querySelector("#dockAddIconText").value = editSite.iconText;
                setDockAddIconSource("text");
              } else if (dockFaviconRuntime.getCustomIcon(editSite.url)) {
                dockAddUploadedIcon = dockFaviconRuntime.getCustomIcon(editSite.url);
                setDockAddIconSource("upload");
              } else {
                setDockAddIconSource("auto");
              }
              syncDockAddPreview();
            } else {
              setDockAddMode("current");
            }
            const openLifecycle = lifecycle;
            entryFrame = requestFrame(() => {
              entryFrame = null;
              if (!destroyed && lifecycle === openLifecycle && dockAddDialog === shell) shell.classList.add("show");
            });
            return true;
          }
          function closeDockAddDialog(restoreFocus = true) {
            if (!dockAddDialog || dockAddSubmitPending) return;
            const shell = dockAddDialog;
            if (entryFrame !== null) cancelFrame(entryFrame);
            entryFrame = null;
            const focusTarget = dockAddRestoreFocus;
            dockAddDialog = null;
            lifecycle += 1;
            dockAddRestoreFocus = null;
            dockAddUploadedIcon = "";
            dockAddEditingSiteId = "";
            dockAddBookmarkRequestSequence += 1;
            dockAddBookmarksLoading = false;
            dockAddBookmarksError = "";
            dockAddBookmarksPermission = null;
            dockAddBookmarksPermissionPending = false;
            dockAddSelectedBookmarkUrls = /* @__PURE__ */ new Set();
            shell.classList.remove("show");
            shell.remove();
            syncHost();
            if (restoreFocus && focusTarget && focusTarget.isConnected && typeof focusTarget.focus === "function") {
              if (focusTarget.classList && focusTarget.classList.contains("web-dock-control")) setExpanded(true);
              focusTarget.focus();
            }
          }
          function isOpen() {
            return Boolean(dockAddDialog);
          }
          function forceClose(restoreFocus = false) {
            dockAddSubmitPending = false;
            return closeDockAddDialog(restoreFocus);
          }
          function containsEvent(event) {
            if (!dockAddDialog || !event) return false;
            const path = typeof event.composedPath === "function" ? event.composedPath() : [];
            return path.includes(dockAddDialog) || Boolean(event.target && dockAddDialog.contains(event.target));
          }
          function refreshLanguage() {
            if (!dockAddDialog || destroyed) return false;
            const editSite = dockAddEditingSiteId ? (context().config?.sites || []).find((site) => site.id === dockAddEditingSiteId) || null : null;
            dialogView.localizeDialog?.(dockAddDialog, editSite);
            renderDockAddBookmarks();
            syncDockAddMode();
            syncDockAddPreview();
            return true;
          }
          function destroy() {
            if (destroyed) return;
            dockAddSubmitPending = false;
            closeDockAddDialog(false);
            destroyed = true;
            lifecycle += 1;
            dockAddBookmarkRequestSequence += 1;
          }
          return {
            open: openDockAddDialog,
            close: closeDockAddDialog,
            forceClose,
            destroy,
            isOpen,
            containsEvent,
            refreshLanguage,
            compressIcon: compressDockAddIcon
          };
        }
        return { createDockAddDialog };
      });
    }
  });

  // src/content/dock-search-dialog.js
  var require_dock_search_dialog = __commonJS({
    "src/content/dock-search-dialog.js"(exports, module) {
      (function(root, factory) {
        const commonJs = typeof module === "object" && module.exports;
        const contract = commonJs ? require_dock_search_contract() : root.TfaDockSearchContract;
        const api = factory(contract);
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockSearchDialog = api;
        else if (!commonJs) root.TfaDockSearchDialog = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function(SearchContract) {
        "use strict";
        const SEARCH_PERMISSIONS_CHANGED_EVENT = "tfa:web-dock-search-permissions-changed";
        const ENGINES = Object.freeze({
          google: { label: "Google", icon: "web-dock-icons/google.svg" },
          bing: { label: "\u5FC5\u5E94", icon: "web-dock-icons/bing.png" },
          baidu: { label: "\u767E\u5EA6", icon: "web-dock-icons/baidu.svg" },
          yandex: { label: "Yandex", icon: "web-dock-icons/yandex.png" }
        });
        function createDockSearchDialog(deps) {
          const {
            chrome: chrome2,
            document: document2,
            window: providedWindow,
            send,
            escapeHtml,
            context,
            closeOthers,
            syncHost,
            showToast = () => {
            },
            setExpanded = () => {
            },
            translate = () => "",
            setTimeoutFn = setTimeout,
            clearTimeoutFn = clearTimeout,
            requestFrame = requestAnimationFrame,
            cancelFrame = typeof cancelAnimationFrame === "function" ? cancelAnimationFrame : () => {
            },
            debounceMs = 140
          } = deps;
          const windowRef = providedWindow || document2?.defaultView || globalThis.window;
          let shell = null;
          let restoreFocusTarget = null;
          let debounceTimer = null;
          let openFrame = null;
          let requestSequence = 0;
          let operationEpoch = 0;
          let activeIndex = -1;
          let suggestions = [];
          let historyPermission = null;
          let bookmarksPermission = null;
          let lastPermissionBroadcastToken = "";
          let engine = "google";
          let destroyed = false;
          function tr(key, fallback, params = {}) {
            const value = translate(key, params);
            const base = value && value !== key ? value : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          }
          function engineButtonsMarkup() {
            return Object.entries(ENGINES).map(([key, meta]) => `
        <button class="dock-search-engine" type="button" data-dock-search-engine="${key}" aria-label="${escapeHtml(tr("dock.search.engineAria", "\u4F7F\u7528 {engine} \u641C\u7D22", { engine: meta.label }))}">
          <img src="${escapeHtml(chrome2.runtime.getURL(meta.icon))}" alt="" draggable="false">
        </button>`).join("");
          }
          function sourceLabel(source) {
            if (source === "common") return tr("dock.search.source.common", "\u5E38\u7528");
            if (source === "fixed") return tr("dock.search.source.fixed", "\u56FA\u5B9A");
            if (source === "tab") return tr("dock.search.source.tab", "\u9875\u7B7E");
            if (source === "bookmark") return tr("dock.search.source.bookmark", "\u4E66\u7B7E");
            if (source === "browser" || source === "history") return tr("dock.search.source.history", "\u5386\u53F2");
            if (source === "web") return tr("dock.search.source.web", "\u7F51\u9875");
            return tr("dock.search.source.unknown", "\u5176\u4ED6");
          }
          function applyPermissions(permissionState = {}) {
            bookmarksPermission = SearchContract.mergePermission(bookmarksPermission, permissionState.bookmarks);
            historyPermission = SearchContract.mergePermission(historyPermission, permissionState.history);
          }
          function handlePermissionsChanged(message = {}) {
            if (destroyed || message.type !== "WEB_DOCK_SEARCH_PERMISSIONS_CHANGED") return false;
            const token = String(message.token || "");
            if (token && token === lastPermissionBroadcastToken) return false;
            if (token) lastPermissionBroadcastToken = token;
            applyPermissions(message.permissions);
            if (!shell) return true;
            requestSequence += 1;
            render();
            const query = shell.querySelector("#dockSearchInput")?.value?.trim() || "";
            if (query) schedule({ immediate: true });
            return true;
          }
          function onPermissionsChangedEvent(event) {
            handlePermissionsChanged(event?.detail);
          }
          function permissionMarkup(permission, kind) {
            if (!permission || permission.granted === true) return "";
            const bookmarks = kind === "bookmarks";
            const fallback = bookmarks ? tr("permission.bookmarks.required", "\u4E66\u7B7E\u672A\u6388\u6743\uFF1B\u9875\u7B7E\u548C\u5386\u53F2\u7ED3\u679C\u4ECD\u53EF\u7528\u3002") : tr("permission.history.required", "\u5386\u53F2\u8BB0\u5F55\u672A\u6388\u6743\uFF1B\u9875\u7B7E\u548C\u4E66\u7B7E\u7ED3\u679C\u4ECD\u53EF\u7528\u3002");
            const action = permission.recoverable === false ? "" : `<button class="dock-search-permission" type="button" data-dock-search-${kind}-permission>${escapeHtml(bookmarks ? tr("permission.bookmarks.allow", "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E") : tr("permission.history.allow", "\u5141\u8BB8\u8BFB\u53D6\u6D4F\u89C8\u5668\u5386\u53F2"))}</button>`;
            const message = permission.message && !(tr("locale.id", "zh-CN").startsWith("en") && /[\p{Script=Han}]/u.test(permission.message)) ? permission.message : fallback;
            return `<div class="dock-search-permission-status" role="status"><span>${escapeHtml(message)}</span>${action}</div>`;
          }
          function render(recents = null) {
            if (!shell || destroyed) return;
            const input = shell.querySelector("#dockSearchInput");
            const results = shell.querySelector("#dockSearchResults");
            const clear = shell.querySelector("#dockSearchClear");
            if (!input || !results || !clear) return;
            const query = input.value.trim();
            clear.hidden = !input.value;
            activeIndex = -1;
            if (!query) {
              results.dataset.mode = "recents";
              const recentItems = Array.isArray(recents) ? recents : Array.from(results.querySelectorAll("[data-dock-search-recent]")).map((node) => ({
                query: node.dataset.dockSearchRecent || "",
                engine: node.dataset.dockSearchEngine || "google"
              }));
              results.innerHTML = `<div class="dock-search-results-heading"><span>${escapeHtml(tr("dock.search.recents", "\u6700\u8FD1\u641C\u7D22"))}</span><small>${recentItems.length ? escapeHtml(tr("dock.search.resultCount", "{count} \u6761", { count: recentItems.length })) : ""}</small></div>
          <div class="dock-search-result-list">${recentItems.length ? recentItems.map((item) => `
            <button class="dock-search-result" type="button" data-dock-search-recent="${escapeHtml(item.query)}" data-dock-search-engine="${escapeHtml(item.engine || "google")}">
              <span class="dock-search-result-symbol" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12 8v4l2.5 2.5"/><path d="M3.05 11a9 9 0 1 1 .5 4M3 16v-5h5"/></svg></span>
              <span><strong>${escapeHtml(item.query)}</strong><small>${escapeHtml(ENGINES[item.engine]?.label || "Google")}</small></span><span class="dock-search-result-enter" aria-hidden="true">\u21B5</span>
            </button>`).join("") : `<div class="dock-search-empty">${escapeHtml(tr("dock.search.noRecents", "\u6682\u65E0\u6700\u8FD1\u641C\u7D22\uFF0C\u8F93\u5165\u5173\u952E\u8BCD\u5F00\u59CB\u641C\u7D22"))}</div>`}</div>`;
              return;
            }
            results.dataset.mode = "suggestions";
            const permissionAction = [
              permissionMarkup(bookmarksPermission, "bookmarks"),
              permissionMarkup(historyPermission, "history")
            ].join("");
            const permissionMissing = bookmarksPermission?.granted === false || historyPermission?.granted === false;
            const emptyCopy = permissionMissing ? tr("dock.search.emptyPermission", "\u672A\u627E\u5230\u5339\u914D\u7684\u53EF\u7528\u7ED3\u679C\uFF1B\u6388\u6743\u540E\u5C06\u81EA\u52A8\u8865\u5145\u5BF9\u5E94\u7ED3\u679C\u3002") : tr("dock.search.empty", "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\uFF1B\u6309 Enter \u4F7F\u7528\u5F53\u524D\u641C\u7D22\u5F15\u64CE\u641C\u7D22");
            results.innerHTML = `<div class="dock-search-results-heading"><span>${escapeHtml(tr("dock.search.results", "\u76F8\u5173\u7ED3\u679C"))}</span><small>${escapeHtml(tr("dock.search.sources", "\u9875\u7B7E \xB7 \u4E66\u7B7E \xB7 \u6D4F\u89C8\u5386\u53F2"))}</small></div>
        ${permissionAction}<div class="dock-search-result-list">${suggestions.length ? suggestions.map((item, index) => `
          <button class="dock-search-result" type="button" data-dock-search-suggestion-index="${index}">
            <span class="dock-search-source dock-search-source-${escapeHtml(item.source)}">${sourceLabel(item.source)}</span>
            <span><strong>${escapeHtml(item.title || item.url)}</strong><small>${escapeHtml(item.url)}</small></span><span class="dock-search-result-enter" aria-hidden="true">\u2197</span>
          </button>`).join("") : `<div class="dock-search-empty">${emptyCopy}</div>`}</div>`;
          }
          async function execute(query, selectedEngine = engine) {
            if (destroyed) return false;
            const normalized = String(query || "").trim().replace(/\s+/g, " ");
            if (!normalized) {
              shell?.querySelector("#dockSearchInput")?.focus({ preventScroll: true });
              return false;
            }
            close(false);
            const operation = ++operationEpoch;
            const response = await send({ type: "DOCK_SEARCH_OPEN", query: normalized, engine: selectedEngine });
            if (destroyed || operation !== operationEpoch) return false;
            if (!response || response.ok !== true) showToast(response?.message || tr("dock.search.openPageFailed", "\u6253\u5F00\u641C\u7D22\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"), "error", 2400);
            return Boolean(response && response.ok);
          }
          async function activate(index) {
            if (destroyed) return false;
            const suggestion = suggestions[index];
            if (!suggestion) return false;
            close(false);
            const operation = ++operationEpoch;
            const response = suggestion.source === "tab" && Number.isInteger(suggestion.tabId) ? await send({ type: "SWITCH_TAB", tabId: suggestion.tabId }) : await send({ type: "DOCK_OPEN_SITE", url: suggestion.url });
            if (destroyed || operation !== operationEpoch) return false;
            if (!response || response.ok !== true) showToast(tr("dock.search.openResultFailed", "\u6253\u5F00\u641C\u7D22\u7ED3\u679C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"), "error", 2200);
            return Boolean(response && response.ok);
          }
          function schedule(options = {}) {
            if (!shell || destroyed) return;
            if (debounceTimer !== null) clearTimeoutFn(debounceTimer);
            debounceTimer = null;
            const input = shell.querySelector("#dockSearchInput");
            const query = input ? input.value.trim() : "";
            const sequence = ++requestSequence;
            if (!query) {
              suggestions = [];
              send({ type: "DOCK_SEARCH_GET_RECENTS" }).then((response) => {
                if (!shell || destroyed || sequence !== requestSequence) return;
                render(response && response.ok ? response.recents : []);
              });
              return;
            }
            const clear = shell.querySelector("#dockSearchClear");
            if (clear) clear.hidden = false;
            const results = shell.querySelector("#dockSearchResults");
            if (results && results.dataset.mode === "recents") render();
            debounceTimer = setTimeoutFn(async () => {
              debounceTimer = null;
              const response = await send({ type: "DOCK_SEARCH_SUGGEST", query, limit: 10, requestId: sequence });
              if (!shell || destroyed || sequence !== requestSequence) return;
              suggestions = response && response.ok ? [...SearchContract.composeSuggestions(response.suggestions)] : [];
              const historyStatus = response?.permissions?.history;
              const bookmarksStatus = response?.permissions?.bookmarks;
              const fallbackHistory = historyStatus?.granted === false || response?.code && String(response.code).startsWith("HISTORY_PERMISSION_") ? {
                granted: false,
                code: historyStatus?.code || response.code,
                message: historyStatus?.message || response.message,
                recoverable: historyStatus?.recoverable ?? response.recoverable
              } : historyStatus;
              applyPermissions({
                history: fallbackHistory,
                bookmarks: bookmarksStatus
              });
              render();
            }, options.immediate === true ? 0 : debounceMs);
          }
          function open(target = null, initialQuery = "") {
            if (destroyed) return false;
            if (shell) {
              shell.querySelector("#dockSearchInput")?.focus({ preventScroll: true });
              return true;
            }
            closeOthers("search");
            const value = context();
            restoreFocusTarget = target || value.searchButton || null;
            suggestions = [];
            historyPermission = null;
            bookmarksPermission = null;
            activeIndex = -1;
            const next = document2.createElement("div");
            next.className = "dock-search-shell";
            next.innerHTML = `<div class="dock-search-overlay" data-dock-search-dismiss></div>
        <section class="dock-search-dialog" role="dialog" aria-modal="true" aria-labelledby="dockSearchTitle">
          <header class="dock-search-header"><div><span>WEB SEARCH</span><h2 id="dockSearchTitle">${escapeHtml(tr("dock.search.title", "\u7F51\u9875\u641C\u7D22"))}</h2><p>${escapeHtml(tr("dock.search.description", "\u5FEB\u901F\u641C\u7D22\u5F53\u524D\u9875\u7B7E\u3001\u4E66\u7B7E\u4E0E\u6D4F\u89C8\u5386\u53F2"))}</p></div><button class="dock-search-close" id="dockSearchClose" type="button" aria-label="${escapeHtml(tr("dock.search.close", "\u5173\u95ED\u7F51\u9875\u641C\u7D22"))}">\xD7</button></header>
          <div class="dock-search-body">
            <div class="dock-search-toolbar">
              <label class="dock-search-input-wrap" for="dockSearchInput">
                <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4 -4"/></svg>
                <input id="dockSearchInput" type="search" maxlength="500" autocomplete="off" spellcheck="false" placeholder="${escapeHtml(tr("dock.search.placeholder", "\u641C\u7D22\u7F51\u9875\u3001\u9875\u7B7E\u6216\u4E66\u7B7E"))}">
                <button class="dock-search-clear" id="dockSearchClear" type="button" aria-label="${escapeHtml(tr("dock.search.clear", "\u6E05\u7A7A\u641C\u7D22\u5185\u5BB9"))}" hidden>\xD7</button>
              </label>
              <div class="dock-search-engines" role="group" aria-label="${escapeHtml(tr("dock.search.chooseEngine", "\u9009\u62E9\u641C\u7D22\u5F15\u64CE"))}">${engineButtonsMarkup()}</div>
            </div>
            <div class="dock-search-results" id="dockSearchResults" aria-live="polite"></div>
          </div>
        </section>`;
            value.root.appendChild(next);
            shell = next;
            syncHost();
            next.addEventListener("wheel", (event) => event.stopPropagation(), { passive: true });
            next.querySelector("#dockSearchClose").addEventListener("click", () => close());
            next.querySelector("[data-dock-search-dismiss]").addEventListener("click", () => close());
            const input = next.querySelector("#dockSearchInput");
            input.value = SearchContract.normalizeQuery(initialQuery);
            next.querySelector("#dockSearchClear").addEventListener("click", () => {
              input.value = "";
              schedule();
              input.focus({ preventScroll: true });
            });
            next.querySelector(".dock-search-engines").addEventListener("click", (event) => {
              const button = event.target.closest("[data-dock-search-engine]");
              if (!button || !ENGINES[button.dataset.dockSearchEngine]) return;
              engine = button.dataset.dockSearchEngine;
              if (input.value.trim()) execute(input.value, engine);
              else input.focus({ preventScroll: true });
            });
            next.querySelector("#dockSearchResults").addEventListener("click", async (event) => {
              const permission = event.target.closest("[data-dock-search-history-permission],[data-dock-search-bookmarks-permission]");
              if (permission) {
                permission.disabled = true;
                const isBookmarks = permission.dataset.dockSearchBookmarksPermission !== void 0;
                let response = null;
                try {
                  response = await send({
                    type: isBookmarks ? "DOCK_REQUEST_BOOKMARKS_PERMISSION" : "DOCK_REQUEST_HISTORY_PERMISSION"
                  });
                } catch (_error) {
                }
                {
                  if (!shell || destroyed) return;
                  const granted = response?.granted === true;
                  const nextPermission = granted ? { granted: true } : {
                    granted: false,
                    code: response?.code || (isBookmarks ? "BOOKMARKS_PERMISSION_DENIED" : "HISTORY_PERMISSION_DENIED"),
                    recoverable: response?.recoverable !== false,
                    message: response?.message || (isBookmarks ? tr("permission.bookmarks.denied", "\u672A\u83B7\u5F97\u4E66\u7B7E\u6743\u9650\uFF1B\u5176\u4ED6\u641C\u7D22\u7ED3\u679C\u4ECD\u53EF\u7528\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002") : tr("permission.history.denied", "\u672A\u83B7\u5F97\u5386\u53F2\u8BB0\u5F55\u6743\u9650\uFF1B\u4ECD\u53EF\u641C\u7D22\u5F53\u524D\u9875\u7B7E\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5\u3002"))
                  };
                  if (isBookmarks) bookmarksPermission = nextPermission;
                  else historyPermission = nextPermission;
                  schedule({ immediate: true });
                }
                return;
              }
              const suggestion = event.target.closest("[data-dock-search-suggestion-index]");
              if (suggestion) activate(Number(suggestion.dataset.dockSearchSuggestionIndex));
              else {
                const recent = event.target.closest("[data-dock-search-recent]");
                if (recent) execute(recent.dataset.dockSearchRecent, recent.dataset.dockSearchEngine || engine);
              }
            });
            input.addEventListener("input", schedule);
            next.addEventListener("keydown", (event) => {
              if (event.key !== "Escape") return;
              event.preventDefault();
              event.stopPropagation();
              close();
            });
            input.addEventListener("keydown", (event) => {
              const rows = Array.from(next.querySelectorAll("[data-dock-search-suggestion-index]"));
              if ((event.key === "ArrowDown" || event.key === "ArrowUp") && rows.length) {
                event.preventDefault();
                activeIndex = event.key === "ArrowDown" ? (activeIndex + 1) % rows.length : (activeIndex - 1 + rows.length) % rows.length;
                rows.forEach((row, index) => row.classList.toggle("active", index === activeIndex));
                rows[activeIndex].scrollIntoView({ block: "nearest" });
              } else if (event.key === "Enter") {
                event.preventDefault();
                if (activeIndex >= 0) activate(activeIndex);
                else execute(input.value);
              }
            });
            const sequence = ++requestSequence;
            if (input.value) schedule();
            else {
              send({ type: "DOCK_SEARCH_GET_RECENTS" }).then((response) => {
                if (shell !== next || destroyed || sequence !== requestSequence) return;
                render(response && response.ok ? response.recents : []);
              });
            }
            send({ type: "DOCK_SEARCH_GET_PERMISSIONS" }).then((response) => {
              if (shell !== next || destroyed) return;
              applyPermissions(response?.permissions);
              render();
            });
            openFrame = requestFrame(() => {
              openFrame = null;
              if (shell === next && !destroyed) {
                next.classList.add("show");
                input.focus({ preventScroll: true });
              }
            });
            return true;
          }
          function close(restoreFocus = true) {
            if (!shell) return false;
            const previous = shell;
            shell = null;
            if (openFrame !== null) cancelFrame(openFrame);
            openFrame = null;
            requestSequence += 1;
            suggestions = [];
            activeIndex = -1;
            if (debounceTimer !== null) clearTimeoutFn(debounceTimer);
            debounceTimer = null;
            previous.classList.remove("show");
            previous.remove();
            syncHost();
            const target = restoreFocusTarget;
            restoreFocusTarget = null;
            if (restoreFocus && target?.isConnected && typeof target.focus === "function") {
              if (target.classList?.contains("web-dock-control")) setExpanded(true);
              target.focus();
            }
            return true;
          }
          function containsEvent(event) {
            if (!shell || !event) return false;
            const path = typeof event.composedPath === "function" ? event.composedPath() : [];
            return path.includes(shell) || Boolean(event.target && shell.contains(event.target));
          }
          function refreshLanguage() {
            if (!shell || destroyed) return false;
            const setText = (selector, value) => {
              const node = shell.querySelector(selector);
              if (node) node.textContent = value;
            };
            const setAttribute = (selector, name, value) => shell.querySelector(selector)?.setAttribute?.(name, value);
            setText("#dockSearchTitle", tr("dock.search.title", "\u7F51\u9875\u641C\u7D22"));
            setText(".dock-search-header p", tr("dock.search.description", "\u5FEB\u901F\u641C\u7D22\u5F53\u524D\u9875\u7B7E\u3001\u4E66\u7B7E\u4E0E\u6D4F\u89C8\u5386\u53F2"));
            setAttribute("#dockSearchClose", "aria-label", tr("dock.search.close", "\u5173\u95ED\u7F51\u9875\u641C\u7D22"));
            setAttribute("#dockSearchInput", "placeholder", tr("dock.search.placeholder", "\u641C\u7D22\u7F51\u9875\u3001\u9875\u7B7E\u6216\u4E66\u7B7E"));
            setAttribute("#dockSearchClear", "aria-label", tr("dock.search.clear", "\u6E05\u7A7A\u641C\u7D22\u5185\u5BB9"));
            setAttribute(".dock-search-engines", "aria-label", tr("dock.search.chooseEngine", "\u9009\u62E9\u641C\u7D22\u5F15\u64CE"));
            shell.querySelectorAll?.("[data-dock-search-engine]")?.forEach?.((button) => {
              const meta = ENGINES[button.dataset.dockSearchEngine];
              if (meta) button.setAttribute("aria-label", tr("dock.search.engineAria", "\u4F7F\u7528 {engine} \u641C\u7D22", { engine: meta.label }));
            });
            render();
            return true;
          }
          function destroy() {
            if (destroyed) return;
            close(false);
            destroyed = true;
            requestSequence += 1;
            operationEpoch += 1;
            windowRef?.removeEventListener?.(SEARCH_PERMISSIONS_CHANGED_EVENT, onPermissionsChangedEvent);
          }
          function isOpen() {
            return Boolean(shell);
          }
          function inspect() {
            return {
              destroyed,
              open: isOpen(),
              requestSequence,
              debouncePending: debounceTimer !== null,
              activeIndex,
              engine,
              permissions: { bookmarks: bookmarksPermission, history: historyPermission },
              suggestions: suggestions.map((suggestion) => ({ ...suggestion }))
            };
          }
          windowRef?.addEventListener?.(SEARCH_PERMISSIONS_CHANGED_EVENT, onPermissionsChangedEvent);
          return { open, close, destroy, containsEvent, isOpen, refreshLanguage, schedule, execute, handlePermissionsChanged, inspect };
        }
        return { createDockSearchDialog, ENGINES, SEARCH_PERMISSIONS_CHANGED_EVENT };
      });
    }
  });

  // src/content/dock-dialog-runtime.js
  var require_dock_dialog_runtime = __commonJS({
    "src/content/dock-dialog-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDockDialogRuntime = api;
        else if (!commonJs) root.TfaDockDialogRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function createDockDialogRuntime(deps) {
          const { settings, add, search, activation, closeContextMenu = () => {
          }, closeMenus = () => {
          } } = deps;
          let destroyed = false;
          function prepare() {
            closeContextMenu();
            closeMenus();
          }
          function openAdd(...args) {
            if (destroyed) return false;
            prepare();
            search.close(false);
            return add.open(...args);
          }
          function openSearch(...args) {
            if (destroyed) return false;
            prepare();
            add.forceClose(false);
            settings.forceClose(false);
            return search.open(...args);
          }
          function openSettings(...args) {
            if (destroyed) return false;
            prepare();
            search.close(false);
            add.forceClose(false);
            return settings.open(...args);
          }
          function suppress(event) {
            if (!event) return;
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();
          }
          function closeTopmost(event, restoreFocus = true) {
            let closed = false;
            if (search.isOpen()) {
              search.close(restoreFocus);
              closed = true;
            } else if (add.isOpen()) {
              add.close(restoreFocus);
              closed = true;
            } else if (activation.hasPurchasePreview()) {
              activation.closePurchasePreview(restoreFocus);
              closed = true;
            } else if (activation.hasDeactivationDialog()) {
              activation.closeDeactivationDialog(restoreFocus);
              closed = true;
            } else if (settings.isOpen()) {
              closed = settings.close(restoreFocus) !== false;
            }
            if (closed) suppress(event);
            return closed;
          }
          function hasOpen() {
            return settings.isOpen() || add.isOpen() || search.isOpen() || activation.hasPurchasePreview() || activation.hasDeactivationDialog();
          }
          function containsEvent(event) {
            return settings.containsEvent(event) || add.containsEvent(event) || search.containsEvent(event);
          }
          function forceTeardown({ destroy: destroy2 = false } = {}) {
            search.close(false);
            add.forceClose(false);
            settings.forceClose(false);
            activation.forceCloseLayers();
            if (destroy2) {
              search.destroy();
              add.destroy();
              settings.destroy();
            }
          }
          function destroy() {
            if (destroyed) return;
            destroyed = true;
            forceTeardown({ destroy: true });
          }
          return { openAdd, openSearch, openSettings, closeTopmost, hasOpen, containsEvent, forceTeardown, destroy };
        }
        return { createDockDialogRuntime };
      });
    }
  });

  // src/content/web-dock-motion-runtime.js
  var require_web_dock_motion_runtime = __commonJS({
    "src/content/web-dock-motion-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaWebDockMotionRuntime = api;
        else if (!commonJs) root.TfaWebDockMotionRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function springState() {
          return {
            scale: { value: 1, velocity: 0 },
            x: { value: 0, velocity: 0 },
            y: { value: 0, velocity: 0 }
          };
        }
        function createWebDockMotionRuntime(deps) {
          const requestFrame = deps.requestFrame || requestAnimationFrame;
          const cancelFrame = deps.cancelFrame || cancelAnimationFrame;
          const tooltipGutter = Number.isFinite(deps.tooltipGutter) ? deps.tooltipGutter : 8;
          let destroyed = false;
          let paused = false;
          let epoch = 0;
          let pointerX = null;
          let centers = [];
          let labelMetrics = /* @__PURE__ */ new WeakMap();
          let transforms = /* @__PURE__ */ new WeakMap();
          let springs = [];
          let viewport = 0;
          let animationFrame = null;
          let measureFrame = null;
          let lastTimestamp = null;
          let activeItem = null;
          let drag = null;
          let settlingDrag = null;
          let suppressNextClick = false;
          let suppressionTimer = null;
          const current = (value = epoch) => !destroyed && !paused && value === epoch;
          const items = () => Array.from(deps.getItems ? deps.getItems() || [] : []);
          function setActive(next) {
            if (next === activeItem) return;
            if (activeItem) activeItem.removeAttribute("data-dock-active");
            activeItem = next || null;
            if (activeItem) activeItem.dataset.dockActive = "true";
          }
          function writeTransform(item, target, options = {}) {
            if (!item) return;
            const scale = Number.isFinite(target && target.scale) && target.scale > 0 ? target.scale : 1;
            const x = Number.isFinite(target && target.x) ? target.x : 0;
            const y = Number.isFinite(target && target.y) ? target.y : 0;
            const updateLabel = options.updateLabel !== false;
            const metric = labelMetrics.get(item);
            let labelShift = 0;
            let arrowShift = 0;
            if (updateLabel && metric && metric.width > 0 && viewport > 0) {
              const placement = deps.computeTooltipPlacement({
                viewportWidth: viewport,
                itemCenter: metric.center + x,
                tooltipWidth: metric.width,
                gutter: tooltipGutter
              });
              labelShift = Number(placement && placement.labelShift) || 0;
              arrowShift = Number(placement && placement.arrowShift) || 0;
            }
            transforms.set(item, { scale, x, y });
            if (updateLabel) {
              item.style.setProperty("--dock-item-scale", String(scale));
              item.style.setProperty("--dock-label-gap", `${11 / scale}px`);
              item.style.setProperty("--dock-label-shift", `${labelShift / scale}px`);
              item.style.setProperty("--dock-label-arrow-shift", `${arrowShift}px`);
            }
            item.style.transform = `translate3d(${x}px, ${y}px, 0) scale(${scale})`;
          }
          function measure() {
            if (!current()) return false;
            const measuredItems = items();
            viewport = Math.max(0, Number(deps.viewportWidth ? deps.viewportWidth() : 0) || 0);
            const safeTooltipWidth = Math.max(0, viewport - tooltipGutter * 2);
            const shell = deps.getShell ? deps.getShell() : null;
            if (shell) {
              shell.style.setProperty("--dock-tooltip-max-width", `${safeTooltipWidth}px`);
              shell.style.setProperty("--dock-visual-viewport-width", `${viewport}px`);
            }
            const coarse = Boolean(deps.coarsePointer && deps.coarsePointer());
            const measurements = measuredItems.map((item) => {
              const rect = item.getBoundingClientRect();
              const transform = transforms.get(item) || { scale: 1, x: 0, y: 0 };
              const appliedScale = coarse ? 1 : transform.scale;
              const center = rect.left + rect.width / 2 - (coarse ? 0 : transform.x);
              const label = item.querySelector(".web-dock-label, .web-dock-control-label");
              return {
                center,
                itemWidth: rect.width / appliedScale,
                width: Math.min(label ? Number(label.offsetWidth) || 0 : 0, safeTooltipWidth),
                transform
              };
            });
            centers = measurements.map((entry) => entry.center);
            labelMetrics = /* @__PURE__ */ new WeakMap();
            measurements.forEach((entry, index) => labelMetrics.set(measuredItems[index], entry));
            measurements.forEach((entry, index) => writeTransform(measuredItems[index], entry.transform));
            if (springs.length !== measuredItems.length) springs = measuredItems.map(() => springState());
            return true;
          }
          function scheduleMeasure() {
            if (!current() || measureFrame !== null) return false;
            const scheduledEpoch = epoch;
            let id = null;
            id = requestFrame(() => {
              if (measureFrame === id) measureFrame = null;
              if (current(scheduledEpoch)) measure();
            });
            measureFrame = id;
            return true;
          }
          function stop() {
            if (animationFrame !== null) cancelFrame(animationFrame);
            animationFrame = null;
            lastTimestamp = null;
          }
          function cancelMeasure() {
            if (measureFrame !== null) cancelFrame(measureFrame);
            measureFrame = null;
          }
          function reset() {
            stop();
            pointerX = null;
            setActive(null);
            springs = items().map((item) => {
              writeTransform(item, { scale: 1, x: 0, y: 0 });
              return springState();
            });
          }
          function frame(timestamp, scheduledEpoch = epoch) {
            if (!current(scheduledEpoch)) return;
            const animated = items();
            if (!animated.length || animated.length !== centers.length) {
              stop();
              return;
            }
            const config = deps.getConfig();
            const motion = config.motion || {};
            const targets = deps.computeTargets(centers, pointerX, motion, config.appearance || {});
            const reduced2 = Boolean(deps.reducedMotion && deps.reducedMotion());
            const dt = lastTimestamp === null ? 1 / 60 : Math.max(1 / 240, Math.min(1 / 30, (timestamp - lastTimestamp) / 1e3));
            lastTimestamp = timestamp;
            let activeIndex = -1;
            if (pointerX !== null) {
              activeIndex = targets.reduce((best, target, index) => best < 0 || target.scale > targets[best].scale ? index : best, -1);
            }
            setActive(activeIndex < 0 ? null : animated[activeIndex]);
            let settled = true;
            animated.forEach((item, index) => {
              const target = targets[index] || { scale: 1, x: 0, y: 0 };
              let state = springs[index] || springState();
              for (const channel of ["scale", "x", "y"]) {
                state[channel] = reduced2 ? { value: target[channel], velocity: 0 } : deps.stepSpring(state[channel], target[channel], dt, motion);
                if (Math.abs(state[channel].value - target[channel]) >= 1e-3 || Math.abs(state[channel].velocity) >= 1e-3) settled = false;
              }
              springs[index] = state;
              writeTransform(item, {
                scale: state.scale.value,
                x: state.x.value,
                y: state.y.value
              });
            });
            if (reduced2 || settled) {
              animated.forEach((item, index) => writeTransform(item, targets[index] || { scale: 1, x: 0, y: 0 }));
              lastTimestamp = null;
              return;
            }
            queueFrame();
          }
          function queueFrame() {
            if (!current() || animationFrame !== null) return false;
            const scheduledEpoch = epoch;
            let id = null;
            id = requestFrame((timestamp) => {
              if (animationFrame === id) animationFrame = null;
              frame(timestamp, scheduledEpoch);
            });
            animationFrame = id;
            return true;
          }
          function start() {
            if (!current()) return false;
            if (deps.reducedMotion && deps.reducedMotion()) {
              stop();
              frame(Date.now(), epoch);
              return true;
            }
            return queueFrame();
          }
          function setPointer(value) {
            if (!current()) return false;
            pointerX = Number.isFinite(value) ? value : null;
            return start();
          }
          function projectPointer(item, index, value) {
            const center = centers[index];
            if (!Number.isFinite(center) || typeof deps.projectPointer !== "function") return value;
            const transform = transforms.get(item) || { scale: 1, x: 0 };
            const metric = labelMetrics.get(item);
            const config = deps.getConfig();
            const baseWidth = Number(metric && metric.itemWidth) || Number(config.appearance && config.appearance.tileSize) || 1;
            const scale = Number.isFinite(transform.scale) && transform.scale > 0 ? transform.scale : 1;
            const visualWidth = Math.max(1, baseWidth * scale);
            const visualCenter = center + (Number(transform.x) || 0);
            return deps.projectPointer(center, value, {
              left: visualCenter - visualWidth / 2,
              width: visualWidth
            }, baseWidth);
          }
          const siteItems = () => Array.from(deps.getSiteItems ? deps.getSiteItems() || [] : []);
          const reduced = () => Boolean(deps.reducedMotion && deps.reducedMotion());
          function cancelFlip(candidate) {
            candidate?.flipAnimations?.forEach((animation) => {
              try {
                animation.cancel();
              } catch (_error) {
              }
            });
            candidate?.flipAnimations?.clear();
          }
          function captureFlip(candidate) {
            const snapshot = /* @__PURE__ */ new Map();
            siteItems().forEach((entry) => {
              if (entry === candidate.item) return;
              const wrap = entry.querySelector(".web-dock-icon-wrap");
              if (wrap) snapshot.set(entry, { wrap, rect: wrap.getBoundingClientRect() });
            });
            return snapshot;
          }
          function animateFlip(snapshot, candidate, enabled = true) {
            cancelFlip(candidate);
            if (!enabled || reduced()) return;
            snapshot.forEach(({ wrap, rect }, entry) => {
              if (!entry.isConnected || !wrap.isConnected || typeof wrap.animate !== "function") return;
              const next = wrap.getBoundingClientRect();
              const x = rect.left - next.left;
              const y = rect.top - next.top;
              if (Math.abs(x) < 0.5 && Math.abs(y) < 0.5) return;
              const animation = wrap.animate([
                { transform: `translate3d(${x}px, ${y}px, 0)` },
                { transform: "translate3d(0, 0, 0)" }
              ], { duration: 230, easing: "cubic-bezier(0.16, 1, 0.3, 1)" });
              candidate.flipAnimations.set(wrap, animation);
              animation.finished.catch(() => {
              }).finally(() => {
                if (candidate.flipAnimations.get(wrap) === animation) candidate.flipAnimations.delete(wrap);
              });
            });
          }
          function movePlaceholder(candidate, insertionIndex, animate = true) {
            const track = deps.getTrack?.();
            if (!track || !candidate.item?.isConnected) return;
            const entries = siteItems();
            const remaining = entries.filter((entry) => entry !== candidate.item);
            const nextIndex = Math.max(0, Math.min(remaining.length, insertionIndex));
            if (entries.indexOf(candidate.item) === nextIndex) return;
            const snapshot = captureFlip(candidate);
            if (nextIndex >= remaining.length) track.appendChild(candidate.item);
            else track.insertBefore(candidate.item, remaining[nextIndex]);
            candidate.insertionIndex = nextIndex;
            animateFlip(snapshot, candidate, animate);
          }
          function restoreOrder(candidate, animate = true) {
            const track = deps.getTrack?.();
            if (!track || !candidate.originalOrder) return cancelFlip(candidate);
            const snapshot = captureFlip(candidate);
            const byUrl = new Map(siteItems().map((entry) => [entry.dataset.dockSiteUrl, entry]));
            candidate.originalOrder.forEach((url) => {
              if (byUrl.get(url)) track.appendChild(byUrl.get(url));
            });
            animateFlip(snapshot, candidate, animate);
          }
          function positionGhost(candidate, event) {
            if (!candidate.ghost) return;
            const left = (Number(event.clientX) || 0) - candidate.ghostWidth * candidate.grabRatioX;
            const top = (Number(event.clientY) || 0) - candidate.ghostHeight * candidate.grabRatioY;
            candidate.ghost.style.transform = `translate3d(${left}px, ${top}px, 0) scale(1.055)`;
          }
          function createGhost(candidate, event) {
            const root = deps.getRoot?.();
            const document2 = deps.getDocument?.();
            const wrap = candidate.item?.querySelector(".web-dock-icon-wrap");
            if (!root || !document2 || !wrap) return false;
            const rect = candidate.item.getBoundingClientRect();
            const ghost = document2.createElement("div");
            const config = deps.getConfig();
            ghost.className = "web-dock-drag-ghost";
            ghost.setAttribute("aria-hidden", "true");
            ghost.style.width = `${rect.width}px`;
            ghost.style.height = `${rect.height}px`;
            ghost.style.setProperty("--dock-icon-size", `${config.motion.iconSize}px`);
            ghost.style.setProperty("--dock-tile-size", `${config.appearance.tileSize}px`);
            ghost.appendChild(wrap.cloneNode(true));
            root.appendChild(ghost);
            candidate.ghost = ghost;
            candidate.ghostWidth = rect.width;
            candidate.ghostHeight = rect.height;
            positionGhost(candidate, event);
            return true;
          }
          function settleGhost(candidate) {
            if (!candidate.ghost || !candidate.item?.isConnected) return Promise.resolve();
            const ghost = candidate.ghost;
            const rect = candidate.item.getBoundingClientRect();
            if (reduced() || typeof ghost.animate !== "function") {
              ghost.remove();
              candidate.ghost = null;
              return Promise.resolve();
            }
            const animation = ghost.animate([
              { transform: ghost.style.transform, opacity: 1 },
              { transform: `translate3d(${rect.left}px, ${rect.top}px, 0) scale(1)`, opacity: 1, offset: 0.72 },
              { transform: `translate3d(${rect.left}px, ${rect.top}px, 0) scale(.96)`, opacity: 0 }
            ], { duration: deps.dragSettleMs || 210, easing: "cubic-bezier(0.16, 1, 0.3, 1)", fill: "forwards" });
            candidate.ghostAnimation = animation;
            return animation.finished.catch(() => {
            }).then(() => {
              if (candidate.ghost === ghost) ghost.remove();
              candidate.ghost = null;
              candidate.ghostAnimation = null;
            });
          }
          function clearSuppression() {
            if (suppressionTimer !== null) (deps.clearTimeoutFn || clearTimeout)(suppressionTimer);
            suppressionTimer = null;
            suppressNextClick = false;
          }
          function releaseCapture(candidate) {
            const track = candidate?.captureTarget;
            if (!track) return;
            try {
              track.removeEventListener?.("lostpointercapture", candidate.lostCapture);
            } catch (_error) {
            }
            try {
              if (!track.hasPointerCapture || track.hasPointerCapture(candidate.pointerId)) {
                track.releasePointerCapture?.(candidate.pointerId);
              }
            } catch (_error) {
            }
            candidate.captureTarget = null;
          }
          function cancelDrag(options = {}) {
            const candidate = drag;
            drag = null;
            if (!candidate) {
              if (options.clearClickSuppression) clearSuppression();
              return false;
            }
            releaseCapture(candidate);
            if (candidate.ghostAnimation && !options.preserveGhost) candidate.ghostAnimation.cancel?.();
            if (candidate.ghost && !options.preserveGhost) candidate.ghost.remove();
            if (candidate.active && !candidate.committed && options.restoreOrder !== false) restoreOrder(candidate, options.animateRestore !== false);
            else cancelFlip(candidate);
            candidate.item?.removeAttribute("data-dock-dragging");
            candidate.item?.removeAttribute("data-dock-placeholder");
            deps.getShell?.()?.removeAttribute("data-reordering");
            if (candidate.active && options.resetMotion !== false) reset();
            if (!options.preserveClickSuppression) clearSuppression();
            else if (suppressNextClick) {
              if (suppressionTimer !== null) (deps.clearTimeoutFn || clearTimeout)(suppressionTimer);
              suppressionTimer = (deps.setTimeoutFn || setTimeout)(() => {
                suppressionTimer = null;
                if (!drag) suppressNextClick = false;
              }, 0);
            }
            return true;
          }
          function cancelSettling() {
            const candidate = settlingDrag;
            settlingDrag = null;
            if (!candidate) return;
            candidate.ghostAnimation?.cancel?.();
            candidate.ghost?.remove();
            candidate.ghost = null;
          }
          function beginDrag(event, item) {
            if (!current() || drag || event?.pointerId == null || event.button != null && event.button !== 0) return false;
            const entries = siteItems();
            const index = entries.indexOf(item);
            const config = deps.getConfig();
            const siteIndex = config.sites[index]?.url === item.dataset.dockSiteUrl ? index : config.sites.findIndex((site) => site.url === item.dataset.dockSiteUrl);
            if (siteIndex < 0) return false;
            const rect = item.getBoundingClientRect();
            clearSuppression();
            drag = {
              pointerId: event.pointerId,
              startX: Number(event.clientX) || 0,
              startY: Number(event.clientY) || 0,
              fromIndex: siteIndex,
              insertionIndex: siteIndex,
              item,
              active: false,
              committed: false,
              finishing: false,
              flipAnimations: /* @__PURE__ */ new Map(),
              grabRatioX: Math.max(0, Math.min(1, ((Number(event.clientX) || 0) - rect.left) / Math.max(1, rect.width))),
              grabRatioY: Math.max(0, Math.min(1, ((Number(event.clientY) || 0) - rect.top) / Math.max(1, rect.height)))
            };
            return true;
          }
          function activateDrag(event) {
            const candidate = drag;
            const track = deps.getTrack?.();
            if (!candidate || !track?.setPointerCapture) {
              cancelDrag();
              return false;
            }
            try {
              track.setPointerCapture(event.pointerId);
              if (track.hasPointerCapture && !track.hasPointerCapture(event.pointerId)) {
                cancelDrag();
                return false;
              }
            } catch (_error) {
              cancelDrag();
              return false;
            }
            candidate.active = true;
            candidate.originalConfig = deps.normalizeConfig(deps.getConfig());
            candidate.originalOrder = candidate.originalConfig.sites.map((site) => site.url);
            candidate.captureTarget = track;
            candidate.lostCapture = () => {
              if (drag === candidate && !candidate.finishing) cancelDrag();
            };
            track.addEventListener("lostpointercapture", candidate.lostCapture);
            suppressNextClick = true;
            reset();
            if (!createGhost(candidate, event)) {
              cancelDrag();
              return false;
            }
            candidate.item.dataset.dockDragging = "true";
            candidate.item.dataset.dockPlaceholder = "true";
            deps.getShell?.()?.setAttribute("data-reordering", "true");
            return true;
          }
          function moveDrag(event) {
            const candidate = drag;
            if (!candidate || candidate.pointerId !== event.pointerId) return false;
            if (!candidate.active) {
              const distance = Math.hypot(
                (Number(event.clientX) || 0) - candidate.startX,
                (Number(event.clientY) || 0) - candidate.startY
              );
              if (distance <= (deps.dragThreshold || 6) || !activateDrag(event)) return false;
            }
            positionGhost(candidate, event);
            const remaining = siteItems().filter((entry) => entry !== candidate.item);
            const centers2 = remaining.map((entry) => {
              const rect = entry.getBoundingClientRect();
              return rect.left + rect.width / 2;
            });
            const found = centers2.findIndex((center) => event.clientX < center);
            movePlaceholder(candidate, found < 0 ? centers2.length : found);
            event.preventDefault?.();
            return true;
          }
          function finishDrag(event) {
            const candidate = drag;
            if (!candidate || candidate.pointerId !== event.pointerId) return false;
            if (!candidate.active) return cancelDrag();
            if (candidate.finishing) return false;
            candidate.finishing = true;
            const urls = siteItems().map((entry) => entry.dataset.dockSiteUrl).filter(Boolean);
            const optimistic = deps.reorderConfig(candidate.originalConfig, urls);
            const before = deps.getAuthority();
            cancelSettling();
            settlingDrag = candidate;
            settleGhost(candidate).finally(() => {
              if (settlingDrag === candidate) settlingDrag = null;
            });
            candidate.committed = true;
            cancelDrag({ preserveClickSuppression: true, preserveGhost: true, restoreOrder: false });
            if (!deps.isCurrent()) return false;
            deps.applyConfig(optimistic, { updateConfirmedBase: false });
            deps.persistOrder(urls).then((result) => {
              if (result.saved || !result.current || !deps.isCurrent()) return;
              const currentAuthority = deps.getAuthority();
              if (currentAuthority.revision === before.revision && currentAuthority.sequence === before.sequence + 1 && deps.sameConfig(currentAuthority.config, optimistic)) {
                deps.applyConfig(candidate.originalConfig, { updateConfirmedBase: false });
              }
              deps.showToast?.("\u6392\u5E8F\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5", "error");
            });
            return true;
          }
          function consumeClickSuppression() {
            if (!suppressNextClick) return false;
            clearSuppression();
            return true;
          }
          function isDragActive() {
            return Boolean(drag && drag.active);
          }
          function pause() {
            if (destroyed || paused) return;
            paused = true;
            epoch += 1;
            stop();
            cancelMeasure();
            cancelDrag({ clearClickSuppression: true, animateRestore: false });
            cancelSettling();
          }
          function resume() {
            if (destroyed || !paused) return false;
            paused = false;
            epoch += 1;
            return true;
          }
          function destroy() {
            if (destroyed) return;
            pause();
            destroyed = true;
            epoch += 1;
            centers = [];
            labelMetrics = /* @__PURE__ */ new WeakMap();
            transforms = /* @__PURE__ */ new WeakMap();
            springs = [];
            setActive(null);
            clearSuppression();
          }
          function inspect() {
            return {
              destroyed,
              paused,
              epoch,
              pointerX,
              viewportWidth: viewport,
              animationFrame,
              measureFrame,
              centerCount: centers.length,
              dragPending: Boolean(drag),
              dragActive: Boolean(drag && drag.active),
              settling: Boolean(settlingDrag),
              suppressNextClick
            };
          }
          return Object.freeze({
            measure,
            scheduleMeasure,
            start,
            stop,
            cancelMeasure,
            reset,
            setPointer,
            projectPointer,
            writeTransform,
            beginDrag,
            moveDrag,
            finishDrag,
            cancelDrag,
            cancelSettling,
            consumeClickSuppression,
            clearClickSuppression: clearSuppression,
            isDragActive,
            pause,
            resume,
            destroy,
            inspect
          });
        }
        return { createWebDockMotionRuntime, createSpringState: springState };
      });
    }
  });

  // src/content/web-dock-recently-closed-runtime.js
  var require_web_dock_recently_closed_runtime = __commonJS({
    "src/content/web-dock-recently-closed-runtime.js"(exports, module) {
      (function attachWebDockRecentlyClosedRuntime(root, factory) {
        const logic = typeof module === "object" && module.exports ? require_web_dock_recently_closed_logic() : root.TfaWebDockRecentlyClosedLogic;
        const api = factory(logic);
        if (typeof module === "object" && module.exports) module.exports = api;
        if (typeof window === "object") window.TfaWebDockRecentlyClosedRuntime = api;
        else root.TfaWebDockRecentlyClosedRuntime = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createModule(logic) {
        "use strict";
        const GRID_ITEM_LIMIT = 15;
        const RECENT_REQUEST_TIMEOUT_MS = 5e3;
        function createWebDockRecentlyClosedRuntime(deps = {}) {
          let items = [];
          let open = false;
          let dirty = true;
          let paused = false;
          let destroyed = false;
          let loading = false;
          let activatingId = "";
          let requestSequence = 0;
          let invalidationVersion = 0;
          let lastBroadcastToken = "";
          let boundRoot = null;
          let boundDocument = null;
          let boundList = null;
          let renderedSignature = "";
          const escapeHtml = deps.escapeHtml || ((value) => String(value || ""));
          const tr = (key, fallback, params = {}) => {
            const translated = deps.translate?.(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          const current = () => !destroyed && !paused && deps.instanceCurrent?.() !== false;
          const context = () => deps.getViewContext?.() || {};
          const setTimeoutFn = deps.setTimeoutFn || setTimeout;
          const clearTimeoutFn = deps.clearTimeoutFn || clearTimeout;
          const requestTimeoutMs = Number.isFinite(Number(deps.recentRequestTimeoutMs)) && Number(deps.recentRequestTimeoutMs) > 0 ? Number(deps.recentRequestTimeoutMs) : RECENT_REQUEST_TIMEOUT_MS;
          function elements() {
            const { root, els = {} } = context();
            return {
              root,
              button: els.webDockRecent,
              panel: els.webDockRecentPanel,
              list: els.webDockRecentList,
              status: els.webDockRecentStatus,
              count: els.webDockRecentCount
            };
          }
          function itemMarkup(item) {
            const icon = deps.iconFor?.(item) || "";
            const fallback = Array.from(item.title.trim())[0] || "?";
            return `<button class="web-dock-recent-item" type="button" data-dock-recent-id="${escapeHtml(item.id)}" data-dock-recent-url="${escapeHtml(item.url)}" aria-label="${escapeHtml(tr("dock.recentRestoreAria", "\u6062\u590D {title}", { title: item.title }))}"><span class="web-dock-recent-icon"><b${icon ? " hidden" : ""}>${escapeHtml(fallback)}</b><img${icon ? ` src="${escapeHtml(icon)}"` : " hidden"} alt="" draggable="false" referrerpolicy="no-referrer"></span><span class="web-dock-recent-label" role="tooltip">${escapeHtml(item.title)}</span></button>`;
          }
          function render() {
            const { list, status, count } = elements();
            if (!list || !status) return false;
            const signature = items.map((item) => `${item.id}
${item.title}
${item.url}
${item.favIconUrl || ""}`).join("\n---\n");
            if (signature !== renderedSignature) {
              list.innerHTML = items.map(itemMarkup).join("");
              renderedSignature = signature;
            }
            list.hidden = !items.length;
            status.hidden = items.length > 0;
            status.textContent = loading ? tr("dock.recentLoading", "\u6B63\u5728\u8BFB\u53D6\u6700\u8FD1\u5173\u95ED\u2026") : tr("dock.recentEmpty", "\u6682\u65E0\u53EF\u6062\u590D\u7684\u7F51\u7AD9");
            if (count) count.textContent = String(items.length);
            return true;
          }
          function setOpen(value, { restoreFocus = false } = {}) {
            const { button, panel } = elements();
            if (!button || !panel) return false;
            open = Boolean(value);
            panel.hidden = !open;
            panel.closest?.(".web-dock-shell")?.setAttribute?.("data-recent-open", String(open));
            deps.setPointerInteractivity?.(open);
            syncDocumentInteractionListeners();
            button.setAttribute("aria-expanded", String(open));
            if (!open && restoreFocus) button.focus?.();
            return true;
          }
          function close(options) {
            if (!open) return false;
            return setOpen(false, options);
          }
          function readRecentlyClosed() {
            let timer = null;
            const timeout = new Promise((resolve) => {
              timer = setTimeoutFn(() => resolve({
                ok: false,
                code: "RECENTLY_CLOSED_TIMEOUT",
                error: "Recently closed request timed out"
              }), requestTimeoutMs);
            });
            let request;
            try {
              request = Promise.resolve(deps.getRecentlyClosed?.());
            } catch (error) {
              request = Promise.resolve({ error: error?.message || String(error) });
            }
            return Promise.race([request, timeout]).finally(() => {
              if (timer !== null) clearTimeoutFn(timer);
            });
          }
          async function refresh() {
            if (!current() || loading) return false;
            const sequence = ++requestSequence;
            const invalidationAtStart = invalidationVersion;
            loading = true;
            render();
            let response;
            try {
              response = await readRecentlyClosed();
            } catch (error) {
              response = { error: error?.message || String(error) };
            }
            if (sequence !== requestSequence) return false;
            loading = false;
            if (!current()) return false;
            const changedDuringRead = invalidationVersion !== invalidationAtStart;
            if (!response || response.error || response.ok === false) {
              const { list, status } = elements();
              if (list) list.hidden = !items.length;
              if (status) {
                status.hidden = items.length > 0;
                status.textContent = tr("dock.recentUnavailable", "\u6700\u8FD1\u5173\u95ED\u6682\u65F6\u65E0\u6CD5\u8BFB\u53D6");
              }
              if (changedDuringRead && open) Promise.resolve().then(() => refresh());
              return false;
            }
            items = Array.isArray(response.dockItems) ? logic.normalizeRecentlyClosed(response.dockItems, GRID_ITEM_LIMIT) : logic.normalizeRecentlyClosed(response.sessions, GRID_ITEM_LIMIT);
            dirty = changedDuringRead;
            render();
            if (dirty && open) Promise.resolve().then(() => refresh());
            return items.slice();
          }
          async function toggle() {
            if (!current() || !attach()) return false;
            if (open) return close({ restoreFocus: true });
            deps.closeOthers?.();
            setOpen(true);
            if (dirty) await refresh();
            elements().list?.querySelector?.("[data-dock-recent-id]")?.focus?.();
            return open;
          }
          async function activate(itemId) {
            if (!current() || activatingId) return false;
            const item = items.find((candidate) => candidate.id === String(itemId || ""));
            if (!item) return false;
            activatingId = item.id;
            let response;
            try {
              response = item.sessionId && item.sessionType !== "window" ? await deps.restoreSession?.(item.sessionId) : await deps.openTab?.(item.url, true);
            } catch (error) {
              response = { error: error?.message || String(error) };
            }
            try {
              if (!current()) return false;
              if (!response || response.error || response.ok === false) {
                deps.showToast?.(tr("dock.recentRestoreFailed", "\u6062\u590D\u7F51\u7AD9\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"), "error", 2200);
                return false;
              }
              if (item.sessionType === "url") {
                let consumed;
                try {
                  consumed = await deps.consumeRecentlyClosedItem?.(item.id, item.url);
                } catch (error) {
                  consumed = { error: error?.message || String(error) };
                }
                if (!consumed || consumed.error || consumed.ok === false) {
                  deps.showToast?.(tr("dock.recentSyncFailed", "\u7F51\u7AD9\u5DF2\u6253\u5F00\uFF0C\u4F46\u6700\u8FD1\u5173\u95ED\u5217\u8868\u540C\u6B65\u5931\u8D25"), "error", 2400);
                  dirty = true;
                  return false;
                }
              }
              items = items.filter((candidate) => candidate.id !== item.id);
              renderedSignature = "";
              render();
              dirty = true;
              close();
              deps.settleForTabSwitch?.();
              deps.showToast?.(tr("dock.recentRestored", "\u5DF2\u6062\u590D\u7F51\u7AD9"), "success", 1600);
              return true;
            } finally {
              activatingId = "";
            }
          }
          function onListClick(event) {
            const button = event.target?.closest?.("[data-dock-recent-id]");
            if (button) activate(button.dataset.dockRecentId);
          }
          function onListError(event) {
            const image = event.target?.closest?.(".web-dock-recent-icon img");
            if (!image) return;
            image.hidden = true;
            const fallback = image.parentElement?.querySelector?.("b");
            if (fallback) fallback.hidden = false;
          }
          function pointInside(element, event) {
            if (!element || !Number.isFinite(event?.clientX) || !Number.isFinite(event?.clientY)) return false;
            const rect = element.getBoundingClientRect?.();
            return Boolean(rect && event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom);
          }
          function itemAtPoint(event) {
            const { list } = elements();
            return Array.from(list?.querySelectorAll?.("[data-dock-recent-id]") || []).find((candidate) => pointInside(candidate, event)) || null;
          }
          function onRootPointerDown(event) {
            if (!open) return;
            const { button, panel } = elements();
            const path = event.composedPath?.() || [];
            if (!path.includes(button) && !path.includes(panel) && !pointInside(panel, event)) close();
          }
          function onDocumentClick(event) {
            if (!open || Number.isFinite(event?.button) && event.button !== 0) return;
            const { panel } = elements();
            const path = event.composedPath?.() || [];
            if (path.includes(panel)) return;
            const button = itemAtPoint(event);
            if (!button && !pointInside(panel, event)) return;
            event.preventDefault?.();
            event.stopImmediatePropagation?.();
            if (button) activate(button.dataset.dockRecentId);
          }
          function onDocumentPointerBoundary(event) {
            if (!open) return;
            const { panel } = elements();
            const path = event.composedPath?.() || [];
            if (path.includes(panel) || !itemAtPoint(event) && !pointInside(panel, event)) return;
            event.preventDefault?.();
            event.stopImmediatePropagation?.();
          }
          function syncDocumentInteractionListeners() {
            boundDocument?.removeEventListener?.("pointerdown", onRootPointerDown, true);
            boundDocument?.removeEventListener?.("pointerdown", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("mousedown", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("pointerup", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("mouseup", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("click", onDocumentClick, true);
            if (!open) return;
            boundDocument?.addEventListener?.("pointerdown", onRootPointerDown, true);
            boundDocument?.addEventListener?.("pointerdown", onDocumentPointerBoundary, true);
            boundDocument?.addEventListener?.("mousedown", onDocumentPointerBoundary, true);
            boundDocument?.addEventListener?.("pointerup", onDocumentPointerBoundary, true);
            boundDocument?.addEventListener?.("mouseup", onDocumentPointerBoundary, true);
            boundDocument?.addEventListener?.("click", onDocumentClick, true);
          }
          function onRootKeyDown(event) {
            if (open && event.key === "Escape") {
              event.preventDefault?.();
              close({ restoreFocus: true });
            }
          }
          function detach() {
            boundRoot?.removeEventListener?.("pointerdown", onRootPointerDown, true);
            boundRoot?.removeEventListener?.("keydown", onRootKeyDown);
            boundDocument?.removeEventListener?.("pointerdown", onRootPointerDown, true);
            boundDocument?.removeEventListener?.("pointerdown", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("mousedown", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("pointerup", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("mouseup", onDocumentPointerBoundary, true);
            boundDocument?.removeEventListener?.("click", onDocumentClick, true);
            boundList?.removeEventListener?.("click", onListClick);
            boundList?.removeEventListener?.("error", onListError, true);
            boundRoot = null;
            boundDocument = null;
            boundList = null;
          }
          function attach() {
            if (!current()) return false;
            const { root, button, panel, list } = elements();
            if (!root || !button || !panel || !list) return false;
            if (root === boundRoot && list === boundList) return true;
            detach();
            boundRoot = root;
            boundDocument = root.host?.ownerDocument || null;
            boundList = list;
            renderedSignature = "";
            root.addEventListener?.("pointerdown", onRootPointerDown, true);
            root.addEventListener?.("keydown", onRootKeyDown);
            list.addEventListener?.("click", onListClick);
            list.addEventListener?.("error", onListError, true);
            setOpen(false);
            return true;
          }
          function handleExternalChange(message = {}) {
            const token = String(message.token || "");
            if (token && token === lastBroadcastToken) return false;
            if (token) lastBroadcastToken = token;
            invalidationVersion += 1;
            dirty = true;
            if (open && !loading) refresh();
            return true;
          }
          function refreshLanguage() {
            renderedSignature = "";
            return render();
          }
          function start() {
            return attach();
          }
          function pause() {
            if (destroyed || paused) return false;
            paused = true;
            requestSequence += 1;
            loading = false;
            close();
            return true;
          }
          function resume() {
            if (destroyed || !paused) return false;
            paused = false;
            dirty = true;
            return attach();
          }
          function destroy() {
            if (destroyed) return false;
            close();
            destroyed = true;
            requestSequence += 1;
            loading = false;
            detach();
            items = [];
            renderedSignature = "";
            return true;
          }
          function inspect() {
            return { open, dirty, paused, destroyed, loading, itemCount: items.length, lastBroadcastToken };
          }
          return Object.freeze({ start, attach, toggle, close, refresh, refreshLanguage, activate, handleExternalChange, pause, resume, destroy, inspect });
        }
        return Object.freeze({ createWebDockRecentlyClosedRuntime, GRID_ITEM_LIMIT, RECENT_REQUEST_TIMEOUT_MS });
      });
    }
  });

  // src/content/web-dock-runtime.js
  var require_web_dock_runtime = __commonJS({
    "src/content/web-dock-runtime.js"(exports, module) {
      (function(root, factory) {
        const motionModule = typeof module === "object" && module.exports ? require_web_dock_motion_runtime() : root.TfaWebDockMotionRuntime;
        const api = factory(motionModule);
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaWebDockRuntime = api;
        else if (!commonJs) root.TfaWebDockRuntime = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function(motionModule) {
        "use strict";
        function createWebDockRuntime(deps) {
          const motion = deps.motion || (deps.motionDeps && motionModule ? motionModule.createWebDockMotionRuntime(deps.motionDeps) : null);
          const setTimer = deps.setTimeoutFn || setTimeout;
          const clearTimer = deps.clearTimeoutFn || clearTimeout;
          const now = deps.now || Date.now;
          const recoveryDelays = deps.recoveryDelays || [50, 150, 350];
          const recoveryWindowMs = Number.isFinite(deps.recoveryWindowMs) ? deps.recoveryWindowMs : 2e3;
          const tr = (key, fallback, params = {}) => {
            const translated = deps.translate?.(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          let destroyed = false;
          let paused = false;
          let started = false;
          let epoch = 0;
          let recoveryTimer = null;
          let initializationTimer = null;
          let initializationAttempts = 0;
          let autoHideTimer = null;
          let recoveryAttempts = [];
          let circuitOpen = false;
          let hostObserver = null;
          let fullscreenSuppressed = false;
          let expanded = false;
          let suspended = false;
          let contextMenu = null;
          let contextMenuDismiss = null;
          let contextMenuFocusFrame = null;
          let contextMenuVersion = 0;
          let openRequest = null;
          let pointerItem = null;
          let lastRenderShell = null;
          let lastRenderTrack = null;
          let lastStructureSignature = "";
          let lastGeometrySignature = "";
          const current = (value = epoch) => !destroyed && !paused && value === epoch && (typeof deps.instanceCurrent !== "function" || deps.instanceCurrent());
          async function initialize(initialEpoch = epoch) {
            if (!current(initialEpoch) || typeof deps.loadInitialConfig !== "function") return false;
            const before = authority();
            let config;
            try {
              config = await deps.loadInitialConfig();
            } catch (_error) {
              if (current(initialEpoch)) scheduleInitialization();
              return false;
            }
            const after = authority();
            if (!current(initialEpoch) || after.revision !== before.revision || after.sequence !== before.sequence) return false;
            if (config === void 0) {
              scheduleInitialization();
              return false;
            }
            if (typeof deps.applyAuthoritativeConfig === "function") deps.applyAuthoritativeConfig(config);
            initializationAttempts = 0;
            if (typeof deps.render === "function") deps.render();
            return true;
          }
          function scheduleInitialization() {
            if (!current() || initializationTimer !== null) return false;
            const delays = deps.initializationDelays || [120, 250, 500, 1e3, 1800];
            if (initializationAttempts >= delays.length) return false;
            const delay = delays[initializationAttempts++];
            const scheduledEpoch = epoch;
            initializationTimer = setTimer(() => {
              initializationTimer = null;
              if (current(scheduledEpoch)) initialize(scheduledEpoch);
            }, delay);
            return true;
          }
          function start() {
            if (destroyed || started) return Promise.resolve(false);
            started = true;
            paused = false;
            epoch += 1;
            initializationAttempts = 0;
            motion?.resume?.();
            return initialize(epoch);
          }
          function clearTrackedTimers() {
            for (const key of ["recoveryTimer", "initializationTimer", "autoHideTimer"]) {
              const id = key === "recoveryTimer" ? recoveryTimer : key === "initializationTimer" ? initializationTimer : autoHideTimer;
              if (id !== null) clearTimer(id);
            }
            recoveryTimer = null;
            initializationTimer = null;
            autoHideTimer = null;
          }
          function cancelInitialization() {
            if (initializationTimer !== null) clearTimer(initializationTimer);
            initializationTimer = null;
          }
          function disconnectHostObserver() {
            hostObserver?.disconnect?.();
            hostObserver = null;
          }
          function beginDrag(...args) {
            return motionCall("beginDrag", ...args);
          }
          function moveDrag(...args) {
            return motionCall("moveDrag", ...args);
          }
          function finishDrag(...args) {
            return motionCall("finishDrag", ...args);
          }
          function cancelDrag(...args) {
            return motionCall("cancelDrag", ...args);
          }
          function pause() {
            if (destroyed || paused) return false;
            paused = true;
            epoch += 1;
            initializationAttempts = 0;
            clearTrackedTimers();
            disconnectHostObserver();
            closeContextMenu();
            deps.closeRecentlyClosed?.();
            const openingShell = viewContext().els?.webDockShell;
            if (openingShell) delete openingShell.dataset.opening;
            openRequest = null;
            pointerItem = null;
            cancelDrag();
            motion?.pause?.();
            return true;
          }
          async function resume() {
            if (destroyed) return false;
            if (!paused && started) return false;
            paused = false;
            started = true;
            circuitOpen = false;
            recoveryAttempts = [];
            epoch += 1;
            motion?.resume?.();
            return initialize(epoch);
          }
          function destroy() {
            if (destroyed) return;
            destroyed = true;
            paused = false;
            started = false;
            epoch += 1;
            clearTrackedTimers();
            disconnectHostObserver();
            cancelDrag();
            closeContextMenu();
            deps.closeRecentlyClosed?.();
            openRequest = null;
            pointerItem = null;
            lastRenderShell = null;
            lastRenderTrack = null;
            lastStructureSignature = "";
            lastGeometrySignature = "";
            const openingShell = viewContext().els?.webDockShell;
            if (openingShell) delete openingShell.dataset.opening;
            motion?.destroy?.();
            if (typeof deps.destroyView === "function") deps.destroyView();
          }
          function authority() {
            return typeof deps.getAuthority === "function" ? deps.getAuthority() || {} : { config: void 0, revision: 0, sequence: 0 };
          }
          function viewContext() {
            return typeof deps.getViewContext === "function" ? deps.getViewContext() || {} : {};
          }
          function animatedItems(context = viewContext()) {
            if (typeof deps.getAnimatedItems === "function") return Array.from(deps.getAnimatedItems() || []);
            const { els = {} } = context;
            const sites = els.webDockTrack ? Array.from(els.webDockTrack.querySelectorAll(".web-dock-item")) : [];
            return [els.webDockAdd, els.webDockSearch, ...sites, els.webDockSettings, els.webDockRecent].filter(Boolean);
          }
          function syncInteractivity(options = {}) {
            const {
              root,
              document: document2,
              els = {},
              overlayVisible = false,
              fullscreenSuppressed: fullscreenSuppressed2 = false
            } = viewContext();
            const shell = els.webDockShell;
            const trigger = els.webDockTrigger;
            if (!shell || !trigger) return false;
            const config = authority().config || {};
            const nav = shell.querySelector(".web-dock");
            const siteItems = els.webDockTrack ? Array.from(els.webDockTrack.querySelectorAll(".web-dock-item")) : [];
            const controls = Array.from(shell.querySelectorAll(".web-dock-control"));
            const interactiveItems = animatedItems();
            const hidden = shell.hidden || !config.enabled || fullscreenSuppressed2 || destroyed;
            const viewSuspended = suspended || overlayVisible || shell.dataset.suspended === "true";
            const viewExpanded = !config.autoHide || expanded;
            const dockInteractive = !hidden && !viewSuspended && viewExpanded;
            const triggerInteractive = !hidden && !viewSuspended && config.autoHide && !viewExpanded;
            const active = root && root.activeElement || document2 && document2.activeElement;
            trigger.tabIndex = triggerInteractive ? 0 : -1;
            trigger.setAttribute("aria-hidden", String(!triggerInteractive));
            if (nav) {
              nav.tabIndex = -1;
              nav.setAttribute("aria-hidden", String(!dockInteractive));
              nav.inert = !dockInteractive;
              if (dockInteractive) nav.removeAttribute("inert");
              else nav.setAttribute("inert", "");
            }
            siteItems.forEach((item) => {
              item.tabIndex = dockInteractive ? 0 : -1;
            });
            controls.forEach((control) => {
              control.tabIndex = dockInteractive ? 0 : -1;
            });
            if (interactiveItems.includes(active) && !dockInteractive) {
              if (triggerInteractive) trigger.focus();
              else active.blur?.();
            } else if (active === trigger && !triggerInteractive) {
              if (dockInteractive && interactiveItems[0]) interactiveItems[0].focus();
              else trigger.blur?.();
            }
            if (options.focusFirstItem && dockInteractive) interactiveItems[0]?.focus();
            return true;
          }
          function syncShellVisibility(context = viewContext()) {
            const {
              els = {},
              stylesReady = false,
              fullscreenSuppressed: fullscreenSuppressed2 = false,
              overlayVisible = false
            } = context;
            const shell = els.webDockShell;
            if (!shell) return false;
            const config = authority().config || {};
            const hidden = !stylesReady || !config.enabled || fullscreenSuppressed2 || suspended || overlayVisible || destroyed;
            shell.hidden = hidden;
            shell.style.pointerEvents = hidden ? "none" : "auto";
            shell.dataset.suspended = String(suspended || overlayVisible);
            return true;
          }
          function setExpanded(value, options = {}) {
            if (!current()) return false;
            const config = authority().config || {};
            const next = config.autoHide ? Boolean(value) : true;
            const changed = next !== expanded;
            expanded = next;
            if (expanded && autoHideTimer !== null) {
              clearTimer(autoHideTimer);
              autoHideTimer = null;
            }
            const { els = {} } = viewContext();
            if (els.webDockShell) els.webDockShell.dataset.expanded = String(expanded);
            if (!expanded) motion?.reset?.();
            if (changed || options.focusFirstItem) syncInteractivity(options);
            return changed;
          }
          function setSuspended(value) {
            if (!current()) return false;
            const next = Boolean(value);
            const changed = suspended !== next;
            suspended = next;
            const { els = {} } = viewContext();
            if (els.webDockShell) els.webDockShell.dataset.suspended = String(suspended);
            if (suspended) {
              closeContextMenu();
              deps.closeRecentlyClosed?.();
              cancelDrag();
              motion?.cancelSettling?.();
              setExpanded(false);
            } else setExpanded(!(authority().config || {}).autoHide);
            syncShellVisibility();
            syncInteractivity();
            return changed;
          }
          function clearAutoHide() {
            if (autoHideTimer !== null) clearTimer(autoHideTimer);
            autoHideTimer = null;
          }
          function scheduleAutoHide() {
            clearAutoHide();
            const config = authority().config || {};
            const recentPanelOpen = viewContext().els?.webDockRecentPanel?.hidden === false;
            if (!current() || !config.autoHide || suspended || recentPanelOpen) return false;
            const scheduledEpoch = epoch;
            autoHideTimer = setTimer(() => {
              autoHideTimer = null;
              if (!current(scheduledEpoch) || suspended) return;
              if (viewContext().els?.webDockRecentPanel?.hidden === false) return;
              if (motion?.inspect?.().pointerX === null) setExpanded(false);
            }, Number(config.motion && config.motion.hideDelay) || 0);
            return true;
          }
          function syncConfigState(config = authority().config || {}) {
            clearAutoHide();
            expanded = !config.autoHide;
            if (!config.enabled) {
              cancelDrag();
              motion?.reset?.();
              motion?.cancelMeasure?.();
            }
            return expanded;
          }
          function renderView() {
            if (!current()) return false;
            const {
              host,
              root,
              els = {},
              stylesReady = false,
              fullscreenSuppressed: fullscreenSuppressed2 = false,
              overlayVisible = false,
              pageZoom = 1
            } = viewContext();
            if (!host || !host.isConnected || !root || !els.webDockShell || !els.webDockTrack) return false;
            const config = authority().config || {};
            const shell = els.webDockShell;
            const track = els.webDockTrack;
            const nextMarkup = config.enabled ? (config.sites || []).map((site) => deps.itemMarkup(site)).join("") : "";
            const structureSignature = nextMarkup;
            const geometrySignature = JSON.stringify({
              appearance: config.appearance || {},
              motion: {
                iconSize: config.motion?.iconSize,
                magnification: config.motion?.magnification
              },
              pageZoom
            });
            const replacementView = shell !== lastRenderShell || track !== lastRenderTrack;
            const structureChanged = replacementView || structureSignature !== lastStructureSignature;
            const geometryChanged = replacementView || geometrySignature !== lastGeometrySignature;
            syncShellVisibility({ host, root, els, stylesReady, fullscreenSuppressed: fullscreenSuppressed2, overlayVisible, pageZoom });
            shell.dataset.autoHide = String(Boolean(config.autoHide));
            shell.dataset.expanded = String(config.autoHide ? expanded : true);
            shell.dataset.suspended = String(suspended || overlayVisible);
            shell.style.setProperty("--dock-icon-size", `${config.motion.iconSize}px`);
            shell.style.setProperty("--dock-tile-size", `${config.appearance.tileSize}px`);
            shell.style.setProperty("--dock-tray-padding", `${config.appearance.trayPadding}px`);
            deps.applyAppearance?.(shell, config.appearance);
            shell.style.setProperty("--dock-magnification", String(config.motion.magnification));
            shell.style.setProperty("--dock-gap", `${Math.max(6, Math.round(config.appearance.tileSize * 0.14))}px`);
            shell.style.setProperty("--dock-page-zoom-inverse", String(1 / pageZoom));
            if (!config.enabled) {
              if (replacementView || track.innerHTML) {
                deps.resetFavicons?.();
                track.innerHTML = "";
                motion?.reset?.();
              }
              lastRenderShell = shell;
              lastRenderTrack = track;
              lastStructureSignature = structureSignature;
              lastGeometrySignature = geometrySignature;
              syncInteractivity();
              return true;
            }
            if (structureChanged) {
              closeContextMenu();
              deps.closeRecentlyClosed?.();
              cancelDrag();
              deps.syncFavicons?.();
              track.innerHTML = nextMarkup;
              motion?.reset?.();
              deps.bindFavicons?.(track);
            }
            bindEvents();
            if (structureChanged || geometryChanged) motion?.measure?.();
            syncInteractivity();
            if (structureChanged) deps.resolveFavicons?.();
            lastRenderShell = shell;
            lastRenderTrack = track;
            lastStructureSignature = structureSignature;
            lastGeometrySignature = geometrySignature;
            return true;
          }
          function closeContextMenu() {
            contextMenuVersion += 1;
            const { document: document2 } = viewContext();
            if (contextMenuFocusFrame !== null) deps.cancelFrame?.(contextMenuFocusFrame);
            contextMenuFocusFrame = null;
            if (contextMenuDismiss && document2) {
              document2.removeEventListener?.("pointerdown", contextMenuDismiss, true);
            }
            contextMenuDismiss = null;
            contextMenu?.remove?.();
            contextMenu = null;
          }
          function openContextMenu(site, item, clientX, clientY) {
            const context = viewContext();
            const { root, document: document2, window: window2 } = context;
            if (!current() || !root || !document2 || !site) return false;
            closeContextMenu();
            const menuEpoch = epoch;
            const menu = document2.createElement("div");
            menu.className = "web-dock-context-menu";
            menu.setAttribute("role", "menu");
            menu.innerHTML = `<button type="button" role="menuitem" data-dock-context-action="background">${tr("dock.menu.background", "\u5728\u540E\u53F0\u65B0\u9875\u6253\u5F00")}</button><button type="button" role="menuitem" data-dock-context-action="edit">${tr("dock.menu.edit", "\u7F16\u8F91")}</button><button type="button" role="menuitem" data-dock-context-action="delete">${tr("dock.menu.delete", "\u5220\u9664")}</button>`;
            const width = Number(window2?.innerWidth) || 0;
            const height = Number(window2?.innerHeight) || 0;
            menu.style.left = `${Math.max(8, Math.min(Number(clientX) || 0, width - 132))}px`;
            menu.style.top = `${Math.max(8, Math.min(Number(clientY) || 0, height - 126))}px`;
            menu.addEventListener("click", async (event) => {
              if (!current(menuEpoch) || contextMenu !== menu) return;
              const button = event.target?.closest?.("[data-dock-context-action]");
              if (!button) return;
              const action = button.dataset.dockContextAction;
              closeContextMenu();
              const actionMenuVersion = contextMenuVersion;
              if (action === "background") {
                const response = await deps.openSite?.({ url: site.url, active: false, forceNew: true });
                if (current(menuEpoch) && contextMenuVersion === actionMenuVersion && !response?.ok) {
                  deps.showToast?.(deps.openError?.(response) || tr("dock.openBackgroundFailed", "\u540E\u53F0\u6253\u5F00\u7F51\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"), "error", 2200);
                }
                return Boolean(response?.ok);
              }
              if (action === "edit") return current(menuEpoch) ? deps.openEditor?.(item, site) : false;
              if (action === "delete") {
                const removed = await deps.removeSite?.(site, item);
                if (current(menuEpoch) && contextMenuVersion === actionMenuVersion && !removed) {
                  deps.showToast?.(tr("dock.deleteFailed", "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"), "error", 2200);
                }
              }
            });
            root.appendChild(menu);
            contextMenu = menu;
            contextMenuDismiss = (event) => {
              const path = event.composedPath?.() || [];
              if (!path.includes(menu)) closeContextMenu();
            };
            document2.addEventListener?.("pointerdown", contextMenuDismiss, true);
            contextMenuFocusFrame = deps.requestFrame?.(() => {
              contextMenuFocusFrame = null;
              if (current(menuEpoch) && contextMenu === menu) menu.querySelector("button")?.focus();
            }) ?? null;
            return true;
          }
          async function openSite(site) {
            if (!site || openRequest || !current()) return false;
            const requestEpoch = epoch;
            const { els = {} } = viewContext();
            const openingShell = els.webDockShell || null;
            const request = { epoch: requestEpoch, shell: openingShell };
            openRequest = request;
            if (openingShell) openingShell.dataset.opening = "true";
            deps.settleForTabSwitch?.();
            try {
              const response = await deps.openSite?.(site.url);
              if (!current(requestEpoch) || openRequest !== request) return false;
              if (!response?.ok) deps.showToast?.(deps.openError?.(response) || tr("dock.openFailed", "\u6253\u5F00\u7F51\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"), "error", 2400);
              return Boolean(response?.ok);
            } finally {
              if (openRequest === request) openRequest = null;
              if (openingShell && current(requestEpoch) && viewContext().els?.webDockShell === openingShell) {
                delete openingShell.dataset.opening;
              }
            }
          }
          function bindEvents() {
            const { root, els = {} } = viewContext();
            const shell = els.webDockShell;
            const track = els.webDockTrack;
            const trigger = els.webDockTrigger;
            if (!root || !shell || !track || !trigger || shell.dataset.dockEventsBound === "true") return false;
            const nav = shell.querySelector(".web-dock");
            if (!nav) return false;
            shell.dataset.dockEventsBound = "true";
            root.addEventListener("pointerdown", (event) => {
              if (contextMenu && !(event.composedPath?.() || []).includes(contextMenu)) closeContextMenu();
            }, true);
            root.addEventListener("keydown", (event) => {
              if (event.key === "Escape") closeContextMenu();
            });
            track.addEventListener("pointerdown", (event) => {
              const item = event.target?.closest?.(".web-dock-item");
              if (item) beginDrag(event, item);
            });
            track.addEventListener("contextmenu", (event) => {
              const item = event.target?.closest?.(".web-dock-item");
              const site = item && (authority().config?.sites || []).find((candidate) => candidate.url === item.dataset.dockSiteUrl);
              if (!site) return;
              event.preventDefault?.();
              event.stopPropagation?.();
              cancelDrag({ clearClickSuppression: true, animateRestore: false });
              openContextMenu(site, item, event.clientX, event.clientY);
            });
            nav.addEventListener("pointermove", (event) => {
              if (moveDrag(event)) return;
              const item = event.target?.closest?.(".web-dock-item, .web-dock-control");
              const index = item ? animatedItems().indexOf(item) : -1;
              if (item !== pointerItem) {
                pointerItem = item || null;
                motion?.scheduleMeasure?.();
              }
              motion?.setPointer?.(index >= 0 ? motion?.projectPointer?.(item, index, event.clientX) : event.clientX);
            });
            nav.addEventListener("pointerenter", () => setExpanded(true));
            nav.addEventListener("pointerleave", () => {
              if (motion?.isDragActive?.()) return;
              cancelDrag();
              pointerItem = null;
              motion?.setPointer?.(null);
              scheduleAutoHide();
            });
            trigger.addEventListener("pointerenter", () => setExpanded(true));
            trigger.addEventListener("click", (event) => {
              const opening = !expanded;
              setExpanded(opening, { focusFirstItem: opening && event.detail === 0 });
            });
            els.webDockAdd?.addEventListener("click", () => {
              if (!current()) return;
              deps.closeRecentlyClosed?.();
              deps.openAdd?.(els.webDockAdd);
            });
            els.webDockSearch?.addEventListener("click", () => {
              if (!current()) return;
              deps.closeRecentlyClosed?.();
              deps.openSearch?.(els.webDockSearch);
            });
            els.webDockSettings?.addEventListener("click", () => {
              if (!current()) return;
              deps.closeRecentlyClosed?.();
              deps.openSettings?.(els.webDockSettings);
            });
            els.webDockRecent?.addEventListener("click", () => {
              if (!current()) return;
              deps.toggleRecentlyClosed?.();
            });
            nav.addEventListener("pointerup", (event) => finishDrag(event));
            nav.addEventListener("pointercancel", () => cancelDrag());
            track.addEventListener("click", (event) => {
              closeContextMenu();
              if (motion?.isDragActive?.() || motion?.consumeClickSuppression?.()) {
                event.preventDefault?.();
                event.stopPropagation?.();
                return;
              }
              const item = event.target?.closest?.(".web-dock-item");
              const site = item && (authority().config?.sites || []).find((candidate) => candidate.url === item.dataset.dockSiteUrl);
              if (site) openSite(site);
            });
            return true;
          }
          function scheduleRecovery(recover) {
            if (!current() || circuitOpen || recoveryTimer !== null) return false;
            const timestamp = now();
            recoveryAttempts = recoveryAttempts.filter((time) => timestamp - time < recoveryWindowMs);
            if (recoveryAttempts.length >= recoveryDelays.length) {
              openRecoveryCircuit();
              return false;
            }
            const delay = recoveryDelays[recoveryAttempts.length];
            recoveryAttempts.push(timestamp);
            const scheduledEpoch = epoch;
            recoveryTimer = setTimer(() => {
              recoveryTimer = null;
              if (current(scheduledEpoch)) recover?.();
            }, delay);
            return true;
          }
          function openRecoveryCircuit() {
            if (circuitOpen) return false;
            circuitOpen = true;
            clearTrackedTimers();
            disconnectHostObserver();
            closeContextMenu();
            const openingShell = viewContext().els?.webDockShell;
            if (openingShell) delete openingShell.dataset.opening;
            openRequest = null;
            cancelDrag();
            motion?.stop?.();
            motion?.cancelMeasure?.();
            deps.onRecoveryCircuitOpen?.();
            return true;
          }
          function recoverHost() {
            if (!current() || circuitOpen) return false;
            closeContextMenu();
            const openingShell = viewContext().els?.webDockShell;
            if (openingShell) delete openingShell.dataset.opening;
            openRequest = null;
            return deps.recoverHost?.() !== false;
          }
          function scheduleHostRecovery() {
            closeContextMenu();
            return scheduleRecovery(() => {
              const host = deps.getHost?.();
              if (!host || !host.isConnected) recoverHost();
            });
          }
          function updateHostRecovery() {
            const needed = current() && !circuitOpen && Boolean(deps.needsHostRecovery?.());
            if (!needed) {
              disconnectHostObserver();
              return false;
            }
            if (hostObserver) return true;
            hostObserver = deps.createMutationObserver?.(() => {
              if (!current()) return disconnectHostObserver();
              const host = deps.getHost?.();
              if (host && !host.isConnected) scheduleHostRecovery();
            }) || null;
            hostObserver?.observe?.(deps.getObservationRoot?.(), { childList: true });
            return Boolean(hostObserver);
          }
          function updateFullscreen() {
            const next = Boolean(deps.isFullscreen?.());
            if (next === fullscreenSuppressed) {
              motion?.scheduleMeasure?.();
              return false;
            }
            fullscreenSuppressed = next;
            if (next) {
              closeContextMenu();
              const openingShell = viewContext().els?.webDockShell;
              if (openingShell) delete openingShell.dataset.opening;
              openRequest = null;
              cancelDrag();
            }
            deps.onFullscreenChange?.(next);
            return true;
          }
          function inspect() {
            const motionState = motion?.inspect?.() || null;
            return {
              destroyed,
              paused,
              started,
              epoch,
              circuitOpen,
              recoveryAttemptCount: recoveryAttempts.length,
              recoveryTimerActive: recoveryTimer !== null,
              initializationTimerActive: initializationTimer !== null,
              initializationAttempts,
              autoHideTimerActive: autoHideTimer !== null,
              dragActive: Boolean(motionState && motionState.dragActive),
              expanded,
              suspended,
              fullscreenSuppressed,
              hostObserverActive: Boolean(hostObserver),
              contextMenuOpen: Boolean(contextMenu),
              contextMenuFocusFrameActive: contextMenuFocusFrame !== null,
              openRequestActive: Boolean(openRequest),
              motion: motionState
            };
          }
          function motionCall(name, ...args) {
            if (!motion || typeof motion[name] !== "function") return false;
            return motion[name](...args);
          }
          return Object.freeze({
            start,
            pause,
            resume,
            destroy,
            initialize,
            beginDrag,
            moveDrag,
            finishDrag,
            cancelDrag,
            scheduleRecovery,
            inspect,
            isCurrent: current,
            getEpoch: () => epoch,
            openRecoveryCircuit,
            recoverHost,
            scheduleHostRecovery,
            updateHostRecovery,
            updateFullscreen,
            cancelInitialization,
            measure: (...args) => motionCall("measure", ...args),
            scheduleMeasure: (...args) => motionCall("scheduleMeasure", ...args),
            startMotion: (...args) => motionCall("start", ...args),
            stopMotion: (...args) => motionCall("stop", ...args),
            cancelMeasure: (...args) => motionCall("cancelMeasure", ...args),
            resetMotion: (...args) => motionCall("reset", ...args),
            setPointer: (...args) => motionCall("setPointer", ...args),
            projectPointer: (...args) => motionCall("projectPointer", ...args),
            writeTransform: (...args) => motionCall("writeTransform", ...args),
            cancelSettlingDrag: (...args) => motionCall("cancelSettling", ...args),
            consumeClickSuppression: (...args) => motionCall("consumeClickSuppression", ...args),
            clearClickSuppression: (...args) => motionCall("clearClickSuppression", ...args),
            isDragActive: (...args) => motionCall("isDragActive", ...args),
            renderView,
            bindEvents,
            openContextMenu,
            closeContextMenu,
            openSite,
            syncInteractivity,
            setExpanded,
            setSuspended,
            scheduleAutoHide,
            clearAutoHide,
            syncConfigState
          });
        }
        return { createWebDockRuntime };
      });
    }
  });

  // src/content/v2-ai-settings-client.js
  var require_v2_ai_settings_client = __commonJS({
    "src/content/v2-ai-settings-client.js"(exports, module) {
      (function attachV2AiSettingsClient(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        if (root) root.TfaV2AiSettingsClient = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createV2AiSettingsClientModule() {
        "use strict";
        const AI_PORT_ERROR_CODES = Object.freeze({
          AUTH: "AI_UNAUTHORIZED",
          PAYMENT_REQUIRED: "AI_PAYMENT_REQUIRED",
          FORBIDDEN: "AI_FORBIDDEN",
          NOT_FOUND: "AI_NOT_FOUND",
          BAD_REQUEST: "AI_BAD_REQUEST",
          RATE_LIMIT: "AI_RATE_LIMITED",
          TIMEOUT: "AI_TIMEOUT",
          NETWORK: "AI_NETWORK_ERROR",
          INVALID_SETTINGS: "AI_INVALID_SETTINGS",
          ABORTED: "AI_REQUEST_FAILED",
          SERVER: "AI_SERVER_ERROR",
          HTTP: "AI_PROVIDER_ERROR",
          NON_JSON: "AI_INVALID_RESPONSE",
          EMPTY_RESPONSE: "AI_INVALID_RESPONSE",
          OUTPUT_TOO_LARGE: "AI_INVALID_RESPONSE",
          INVALID_OUTPUT: "AI_INVALID_RESPONSE",
          AI_SETTINGS_STORAGE_ERROR: "AI_SETTINGS_STORAGE_ERROR",
          AI_PERMISSION_DENIED: "AI_PERMISSION_DENIED"
        });
        function safeUpstreamReason(value, secret = "") {
          let text = typeof value === "string" ? value : "";
          const localSecret = String(secret || "").trim();
          if (localSecret) text = text.split(localSecret).join("[redacted]");
          return text.replace(/bearer\s+[^\s,;]+/gi, "Bearer [redacted]").replace(/\b(api[-_ ]?key|token|secret|authorization)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]").replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 240);
        }
        function safeAiBaseUrl(value) {
          const raw = String(value || "").trim();
          try {
            const url = new URL(raw);
            if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) return "";
            return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
          } catch (_error) {
            return "";
          }
        }
        function sanitizeAiSettingsEnvelope(value = {}) {
          if (!value || typeof value !== "object" || Array.isArray(value)) return null;
          const source = value.settings && typeof value.settings === "object" && !Array.isArray(value.settings) ? value.settings : {};
          const baseUrl = safeAiBaseUrl(source.baseUrl), model = String(source.model || "").trim();
          const apiKeyAction = String(value.apiKeyAction || "preserve");
          if (!baseUrl || !model || model.length > 200 || /[\u0000-\u001f\u007f]/.test(model) || !["preserve", "replace", "clear"].includes(apiKeyAction)) return null;
          const envelope = {
            settings: {
              enabled: source.enabled === true,
              autoOrganize: source.autoOrganize !== false,
              provider: "openai-compatible",
              baseUrl,
              model,
              excludeSensitiveSites: source.excludeSensitiveSites !== false
            },
            apiKeyAction
          };
          if (apiKeyAction === "replace") {
            const apiKey = typeof value.apiKey === "string" ? value.apiKey.trim() : "";
            if (!apiKey || apiKey.length > 4096 || /[\u0000-\u001f\u007f]/.test(apiKey)) return null;
            envelope.apiKey = apiKey;
          }
          return envelope;
        }
        function finiteAiPortError(response, fallback = "AI_REQUEST_FAILED", secret = "") {
          const raw = String(response?.code || response?.errorCode || response?.error?.code || "");
          const reason = safeUpstreamReason(response?.upstreamReason, secret);
          return {
            ok: false,
            code: AI_PORT_ERROR_CODES[raw] || (Object.values(AI_PORT_ERROR_CODES).includes(raw) ? raw : fallback),
            ...reason ? { upstreamReason: reason } : {}
          };
        }
        function sanitizePublicAiSettings(response) {
          const source = response?.settings && typeof response.settings === "object" ? response.settings : response;
          const baseUrl = safeAiBaseUrl(source?.baseUrl), model = String(source?.model || "").trim();
          if (!source || typeof source !== "object" || !baseUrl || !model || model.length > 200 || /[\u0000-\u001f\u007f]/.test(model)) return null;
          const timeout = Number(source.timeoutMs);
          return {
            enabled: source.enabled === true,
            autoOrganize: source.autoOrganize !== false,
            provider: "openai-compatible",
            baseUrl,
            model,
            timeoutMs: Number.isInteger(timeout) && timeout > 0 && timeout <= 12e4 ? timeout : 9e4,
            excludeSensitiveSites: source.excludeSensitiveSites !== false,
            hasApiKey: source.hasApiKey === true
          };
        }
        async function sendAiSettingsRequest(send, type, envelope = null) {
          if (typeof send !== "function") return { ok: false, code: "AI_REQUEST_FAILED" };
          const message = envelope ? { type, ...envelope } : { type };
          let response;
          try {
            response = await send(message);
          } catch (_error) {
            return { ok: false, code: "AI_REQUEST_FAILED" };
          }
          if (!response || response.ok === false || response.error || response.errorCode) {
            return finiteAiPortError(response, "AI_REQUEST_FAILED", envelope?.apiKey);
          }
          if (type === "V2_AI_TEST_CONNECTION") return { ok: true };
          const settings = sanitizePublicAiSettings(response);
          return settings ? { ok: true, settings } : { ok: false, code: "AI_INVALID_RESPONSE" };
        }
        function requestV2AiSettingsGet(send) {
          return sendAiSettingsRequest(send, "V2_AI_SETTINGS_GET");
        }
        function requestV2AiSettingsSave(send, value) {
          const envelope = sanitizeAiSettingsEnvelope(value);
          return envelope ? sendAiSettingsRequest(send, "V2_AI_SETTINGS_SAVE", envelope) : Promise.resolve({ ok: false, code: "AI_INVALID_SETTINGS" });
        }
        function requestV2AiTestConnection(send, value) {
          const envelope = sanitizeAiSettingsEnvelope(value);
          return envelope ? sendAiSettingsRequest(send, "V2_AI_TEST_CONNECTION", envelope) : Promise.resolve({ ok: false, code: "AI_INVALID_SETTINGS" });
        }
        async function requestAiOriginPermissionSafe(ports = {}, baseUrl) {
          const safeBaseUrl = safeAiBaseUrl(baseUrl);
          if (!safeBaseUrl) return false;
          if (typeof ports.requestAiOriginPermission === "function") {
            try {
              const response = await ports.requestAiOriginPermission(safeBaseUrl);
              return response === true || response?.granted === true || response?.ok === true;
            } catch (_error) {
              return false;
            }
          }
          return ports.aiHostPermissionAlreadyGranted === true;
        }
        return Object.freeze({
          sanitizeAiSettingsEnvelope,
          requestV2AiSettingsGet,
          requestV2AiSettingsSave,
          requestV2AiTestConnection,
          requestAiOriginPermissionSafe
        });
      });
    }
  });

  // src/content/web-dock-controller.js
  var require_web_dock_controller = __commonJS({
    "src/content/web-dock-controller.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaWebDockController = api;
        else if (!commonJs) root.TfaWebDockController = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        async function requestV2BehaviorClear(send, translate) {
          const fallback = translate?.("privacy.clearBehaviorFailed") || "\u6E05\u9664\u884C\u4E3A\u8F68\u8FF9\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5";
          if (typeof send !== "function") return { ok: false, error: fallback };
          try {
            return await send({ type: "V2_BEHAVIOR_CLEAR" });
          } catch (_error) {
            return { ok: false, error: fallback };
          }
        }
        async function requestV2DataControl(send, type) {
          if (typeof send !== "function") return { ok: false, error: "Data request failed" };
          try {
            return await send({ type });
          } catch (_error) {
            return { ok: false, error: "Data request failed" };
          }
        }
        function createWebDockController(deps = {}) {
          const normalizeConfig = deps.normalizeConfig || ((value) => value || {});
          const normalizeUrl = deps.normalizeUrl || ((value) => String(value || ""));
          const send = deps.send || (async () => ({ error: "unavailable" }));
          const tr = (key, fallback, params = {}) => {
            const translated = deps.translate?.(key, params);
            const base = translated && translated !== key ? translated : fallback;
            return String(base).replace(/\{(\w+)\}/g, (_match, name) => String(params[name] ?? ""));
          };
          let config = normalizeConfig(deps.defaultConfig || {});
          let revision = 0;
          let mutationSequence = 0;
          let lastBroadcastToken = "";
          let pageZoom = 1;
          let zoomRequestSequence = 0;
          let core = null;
          let started = false;
          let paused = false;
          let destroyed = false;
          let applyingBroadcast = false;
          const bridge = deps.bridge || {};
          const instanceCurrent = () => !destroyed && !paused && deps.instanceCurrent?.() !== false;
          const sameConfig = (left, right) => JSON.stringify(normalizeConfig(left)) === JSON.stringify(normalizeConfig(right));
          const authority = () => ({ config, revision, sequence: mutationSequence });
          const snapshot = () => ({ config, revision, mutationSequence, lastBroadcastToken, pageZoom, zoomRequestSequence, started, paused, destroyed });
          const currentGeneration = () => core?.getEpoch?.() || 0;
          const lifecycleCurrent = (generation = currentGeneration()) => instanceCurrent() && core?.isCurrent?.(generation) !== false;
          function responseMessage(response, fallback) {
            const message = response && typeof response.message === "string" && response.message ? response.message : response && typeof response.error === "string" && response.error ? response.error : "";
            if (/extension context invalidated|receiving end does not exist|message port closed|could not establish connection/i.test(message)) {
              return tr("dock.extensionUpdated", "\u6269\u5C55\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u540E\u91CD\u8BD5");
            }
            if (/already exists|duplicate|已存在|已经固定|已固定/i.test(message)) return tr("dock.duplicate", "\u8BE5\u7F51\u7AD9\u5DF2\u7ECF\u5728\u7F51\u9875\u62D3\u5C55\u575E\u4E2D");
            return message || fallback;
          }
          function beginMutation() {
            return { sequence: ++mutationSequence, configRevision: revision, generation: currentGeneration() };
          }
          function shouldApply(request, response) {
            if (!request || !response || !response.ok || !response.config || !lifecycleCurrent(request.generation)) return false;
            if (request.sequence !== mutationSequence) return false;
            return request.configRevision === revision || sameConfig(config, response.config);
          }
          function requestCurrent(request) {
            return Boolean(request && lifecycleCurrent(request.generation) && request.sequence === mutationSequence && request.configRevision === revision);
          }
          function applyConfig(value, options = {}) {
            if (!instanceCurrent()) return false;
            const previous = config;
            const next = normalizeConfig(value);
            config = next;
            deps.projectConfig?.(next, previous, options);
            return next;
          }
          function setOptimisticConfig(value) {
            if (!instanceCurrent()) return false;
            config = normalizeConfig(value);
            return config;
          }
          function applyExternalConfig(value, options = {}) {
            if (!instanceCurrent()) return false;
            core?.cancelInitialization?.();
            if (!applyingBroadcast) {
              revision += 1;
              mutationSequence += 1;
            }
            if (!options.unchanged) applyConfig(value, { syncVisualDraft: true, clearVisualError: true });
            return true;
          }
          async function persistSite(site, options = {}) {
            if (!instanceCurrent()) return { ok: false, current: false };
            const previousSite = site?.id ? config.sites?.find((candidate) => candidate.id === site.id) || null : null;
            const effectiveSite = { ...site || {} };
            let requestedUrl = "";
            try {
              requestedUrl = normalizeUrl(effectiveSite.url);
            } catch (_error) {
            }
            if (previousSite && requestedUrl === previousSite.url && options.replaceIcon !== true && !Object.prototype.hasOwnProperty.call(effectiveSite, "iconUrl") && !Object.prototype.hasOwnProperty.call(effectiveSite, "iconText")) {
              if (previousSite.iconMode === "text" && previousSite.iconText) {
                effectiveSite.iconMode = "text";
                effectiveSite.iconText = previousSite.iconText;
              } else if (previousSite.iconUrl) {
                effectiveSite.iconUrl = previousSite.iconUrl;
                if (previousSite.iconMode === "custom") effectiveSite.iconMode = "custom";
              }
            }
            const request = beginMutation();
            let response;
            try {
              response = await send({ type: "DOCK_UPSERT_SITE", site: effectiveSite });
            } catch (_error) {
              response = null;
            }
            if (!response?.ok || !response.config) return { ok: false, response };
            if (!shouldApply(request, response)) return { ok: true, response, current: false };
            applyConfig(response.config);
            let nextUrl = "";
            try {
              nextUrl = normalizeUrl(effectiveSite.url);
            } catch (_error) {
            }
            if (options.toastMessage) deps.showToast?.(options.toastMessage, "success", 1500);
            return { ok: true, response, current: true, previousSite, nextUrl };
          }
          async function removeSite(siteId, errorElement, options = {}) {
            if (!instanceCurrent()) return false;
            const removedSite = config.sites?.find((site) => site.id === siteId) || null;
            const originalConfig = config;
            const optimistic = options.animateDock === true && removedSite;
            const request = beginMutation();
            if (optimistic) {
              const next = deps.removeSiteFromConfig?.(originalConfig, siteId) || originalConfig;
              deps.animateRemoval?.(options.item, next);
            }
            let response;
            try {
              response = await send({ type: "DOCK_REMOVE_SITE", siteId });
            } catch (_error) {
              response = null;
            }
            if (response?.ok && response.config) {
              if (!shouldApply(request, response)) return true;
              applyConfig(response.config);
              deps.showToast?.(tr("dock.deleted", "\u7F51\u9875\u5DF2\u5220\u9664"), "success", 1500);
              return true;
            }
            const current = requestCurrent(request);
            if (optimistic && current) applyConfig(originalConfig, { updateConfirmedBase: false });
            if (current) deps.showControlError?.(errorElement, responseMessage(response, tr("dock.deleteFailed", "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")));
            return false;
          }
          async function reorderSites(orderedUrls) {
            if (!instanceCurrent()) return { saved: false, current: false };
            const request = beginMutation();
            let response;
            try {
              response = await send({ type: "DOCK_REORDER_SITES", orderedUrls });
            } catch (_error) {
              response = null;
            }
            if (response?.ok && response.config) {
              if (!shouldApply(request, response)) return { saved: true, current: false };
              applyConfig(response.config);
              deps.showToast?.(tr("dock.orderSaved", "\u7F51\u9875\u987A\u5E8F\u5DF2\u4FDD\u5B58"), "success", 1200);
              return { saved: true, current: true };
            }
            const current = requestCurrent(request);
            if (current) deps.onReorderError?.(responseMessage(response, tr("dock.orderSaveFailed", "\u6392\u5E8F\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")));
            return { saved: false, current };
          }
          function handleStorageChange(changes, area) {
            if (!lifecycleCurrent()) return false;
            if (area === "local" && changes?.[deps.customIconsKey]) {
              deps.refreshCustomIcons?.(changes[deps.customIconsKey].newValue);
              return true;
            }
            if (area === "sync" && changes?.[deps.storageKey]) {
              deps.settingsRuntime?.()?.handleStorageChange?.(changes[deps.storageKey].newValue);
              return true;
            }
            if (area === "sync" && changes?.tfa_language) {
              deps.refreshLanguage?.();
              return true;
            }
            return false;
          }
          function applyBroadcast(message) {
            if (!lifecycleCurrent() || !message?.config) return false;
            const token = typeof message.token === "string" ? message.token : "";
            if (token && token === lastBroadcastToken) return false;
            if (token) lastBroadcastToken = token;
            const unchanged = sameConfig(config, message.config);
            core?.cancelInitialization?.();
            revision += 1;
            mutationSequence += 1;
            applyingBroadcast = true;
            try {
              const applied = deps.settingsRuntime?.()?.applyBroadcast?.(message) === true;
              if (unchanged) applyConfig(message.config, { authorityRebase: true });
              return applied || unchanged;
            } finally {
              applyingBroadcast = false;
            }
          }
          async function toggleSite(tab) {
            if (!instanceCurrent() || !tab) return false;
            const request = beginMutation();
            const iconUrl = deps.normalizeIconUrl?.(tab.favIconUrl) || "";
            const site = { title: tab.title || deps.hostOf?.(tab.url) || tab.url, url: tab.url };
            if (iconUrl) site.iconUrl = iconUrl;
            let response;
            try {
              response = await deps.toggleDockSite?.(site);
            } catch (_error) {
              response = null;
            }
            if (!response?.ok || !response.config) {
              if (requestCurrent(request)) deps.showToast?.(responseMessage(response, tr("dock.pinFailed", "\u56FA\u5B9A\u72B6\u6001\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5")), "error", 1800);
              return false;
            }
            if (!shouldApply(request, response)) return false;
            applyConfig(response.config);
            deps.showToast?.(response.fixed ? tr("dock.pinned", "\u5DF2\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E") : tr("dock.unpinned", "\u5DF2\u53D6\u6D88\u56FA\u5B9A"), "success", 1500);
            return true;
          }
          function createCore() {
            if (typeof deps.runtimeFactory !== "function") return null;
            const env = bridge.env || {};
            const motion = bridge.motion || {};
            return deps.runtimeFactory({
              getAuthority: authority,
              getViewContext: () => ({
                host: bridge.getHost?.(),
                root: bridge.getRoot?.(),
                els: bridge.getEls?.(),
                document: env.document,
                window: env.window,
                overlayVisible: bridge.overlayVisible?.(),
                fullscreenSuppressed: core?.inspect?.().fullscreenSuppressed || false,
                stylesReady: bridge.stylesReady?.(),
                pageZoom
              }),
              instanceCurrent: bridge.instanceCurrent,
              getHost: bridge.getHost,
              needsHostRecovery: bridge.needsHostRecovery,
              createMutationObserver: (callback) => new env.MutationObserver(callback),
              getObservationRoot: () => env.document.documentElement,
              recoverHost: bridge.recoverHost,
              onRecoveryCircuitOpen: bridge.onRecoveryCircuitOpen,
              isFullscreen: () => {
                const document2 = env.document;
                const window2 = env.window;
                const screen = env.screen;
                const pageFullscreen = document2.fullscreenElement || document2.webkitFullscreenElement;
                const browserFullscreen = window2.innerHeight >= screen.height - 2 && window2.innerWidth >= screen.width - 2 && window2.outerHeight >= screen.height - 2 && window2.outerWidth >= screen.width - 2;
                return Boolean(pageFullscreen || browserFullscreen);
              },
              onFullscreenChange: bridge.render,
              loadInitialConfig: async () => {
                await refreshZoom();
                await bridge.loadCustomIcons?.();
                try {
                  const stored = await bridge.readStorage?.(deps.storageKey);
                  if (stored && Object.prototype.hasOwnProperty.call(stored, deps.storageKey) && stored[deps.storageKey] !== void 0) return stored[deps.storageKey];
                } catch (_storageError) {
                }
                const response = await send({ type: "DOCK_GET_CONFIG" });
                return response?.ok && response.config ? response.config : void 0;
              },
              applyAuthoritativeConfig: (value) => applyConfig(value, { syncVisualDraft: true, clearVisualError: true }),
              getAnimatedItems: bridge.getAnimatedItems,
              itemMarkup: bridge.itemMarkup,
              applyAppearance: bridge.applyAppearance,
              resetFavicons: bridge.resetFavicons,
              syncFavicons: bridge.syncFavicons,
              bindFavicons: bridge.bindFavicons,
              resolveFavicons: bridge.resolveFavicons,
              openEditor: bridge.openEditor,
              removeSite: (site, item) => removeSite(site.id, null, { animateDock: true, item }),
              openAdd: bridge.openAdd,
              openSearch: bridge.openSearch,
              openSettings: bridge.openSettings,
              toggleRecentlyClosed: bridge.toggleRecentlyClosed,
              closeRecentlyClosed: bridge.closeRecentlyClosed,
              openSite: (url) => send({ type: "DOCK_OPEN_SITE", url }),
              openError: (response) => responseMessage(response, tr("dock.openFailed", "\u6253\u5F00\u7F51\u9875\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5")),
              translate: deps.translate,
              settleForTabSwitch: bridge.settleForTabSwitch,
              motionDeps: {
                getItems: bridge.getAnimatedItems,
                getSiteItems: bridge.getItems,
                getShell: bridge.getShell,
                getTrack: bridge.getTrack,
                getRoot: bridge.getRoot,
                getDocument: () => env.document,
                getConfig: () => config,
                getAuthority: authority,
                normalizeConfig,
                reorderConfig: motion.reorderConfig,
                applyConfig,
                persistOrder: reorderSites,
                sameConfig,
                isCurrent: lifecycleCurrent,
                showToast: deps.showToast,
                dragThreshold: motion.dragThreshold,
                dragSettleMs: motion.dragSettleMs,
                setTimeoutFn: env.setTimeoutFn,
                clearTimeoutFn: env.clearTimeoutFn,
                viewportWidth: () => [
                  Number(env.window.visualViewport?.width),
                  Number(env.document.documentElement?.clientWidth),
                  Number(env.window.innerWidth)
                ].find((width) => Number.isFinite(width) && width > 0) || 0,
                coarsePointer: () => Boolean(env.window.matchMedia?.("(pointer: coarse)").matches),
                reducedMotion: motion.reducedMotion,
                computeTargets: motion.computeTargets,
                computeTooltipPlacement: motion.computeTooltipPlacement,
                projectPointer: motion.projectPointer,
                stepSpring: motion.stepSpring,
                tooltipGutter: motion.tooltipGutter,
                requestFrame: env.requestFrame,
                cancelFrame: env.cancelFrame
              },
              setTimeoutFn: env.setTimeoutFn,
              clearTimeoutFn: env.clearTimeoutFn,
              now: env.now,
              requestFrame: env.requestFrame,
              cancelFrame: env.cancelFrame
            });
          }
          function applyZoom(value) {
            if (!instanceCurrent()) return false;
            zoomRequestSequence += 1;
            const numeric = Number(value);
            const next = Number.isFinite(numeric) && numeric > 0 ? numeric : 1;
            if (Math.abs(next - pageZoom) < 1e-3) return false;
            pageZoom = next;
            deps.projectZoom?.(next);
            return true;
          }
          async function refreshZoom() {
            if (!instanceCurrent()) return false;
            const token = ++zoomRequestSequence;
            let response;
            try {
              response = await send({ type: "DOCK_GET_TAB_ZOOM" });
            } catch (_error) {
              response = null;
            }
            if (!instanceCurrent() || token !== zoomRequestSequence) return false;
            const numeric = Number(response?.ok ? response.zoom : 1);
            const next = Number.isFinite(numeric) && numeric > 0 ? numeric : 1;
            if (Math.abs(next - pageZoom) < 1e-3) return false;
            pageZoom = next;
            deps.projectZoom?.(next);
            return true;
          }
          core = createCore();
          function attachCore(value) {
            if (destroyed || core) return false;
            core = value;
            return true;
          }
          async function start() {
            if (destroyed || started || !core) return false;
            started = true;
            paused = false;
            try {
              const result = await core.start?.();
              if (result === false && core?.inspect?.().started !== true) started = false;
              return result;
            } catch (_error) {
              started = false;
              return false;
            }
          }
          function pause() {
            if (destroyed || paused) return false;
            paused = true;
            mutationSequence += 1;
            zoomRequestSequence += 1;
            return core?.pause?.() !== false;
          }
          async function resume() {
            if (destroyed || !paused) return false;
            try {
              const resumed = await core?.resume?.();
              if (resumed !== false || core?.inspect?.().paused === false) paused = false;
              return resumed;
            } catch (_error) {
              return false;
            }
          }
          function invalidate() {
            if (destroyed) return false;
            mutationSequence += 1;
            return true;
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            started = false;
            paused = false;
            mutationSequence += 1;
            zoomRequestSequence += 1;
            core?.destroy?.();
            return true;
          }
          return Object.freeze({
            snapshot,
            getConfig: () => config,
            authority,
            responseMessage,
            beginMutation,
            shouldApply,
            requestCurrent,
            applyConfig,
            setOptimisticConfig,
            applyExternalConfig,
            persistSite,
            removeSite,
            reorderSites,
            toggleSite,
            handleStorageChange,
            applyBroadcast,
            applyZoom,
            refreshZoom,
            attachCore,
            getCore: () => core,
            start,
            pause,
            resume,
            invalidate,
            destroy,
            isCurrent: lifecycleCurrent
          });
        }
        function createWebDockComposition(deps = {}) {
          const env = deps.env || {};
          const ports = deps.ports || {};
          const limits = deps.limits || {};
          const dock = deps.dockLogic || {};
          const ui = deps.uiLogic || {};
          const aiSettingsClient = deps.aiSettingsClient || globalThis.TfaV2AiSettingsClient;
          const moduleApi = (name) => deps.modules?.[name] || globalThis[name];
          const SettingsRuntime = moduleApi("TfaDockSettingsRuntime");
          const DockRuntime = moduleApi("TfaWebDockRuntime");
          const FaviconRuntime = moduleApi("TfaDockFaviconRuntime");
          const SettingsPreview = moduleApi("TfaDockSettingsPreview");
          const SettingsViewMarkup = moduleApi("TfaDockSettingsViewMarkup"), SettingsBackupSection = moduleApi("TfaDockSettingsBackupSection");
          const SettingsView = moduleApi("TfaDockSettingsView");
          const AddDialog = moduleApi("TfaDockAddDialog");
          const AddDialogView = moduleApi("TfaDockAddDialogView");
          const SearchDialog = moduleApi("TfaDockSearchDialog");
          const DialogRuntime = moduleApi("TfaDockDialogRuntime");
          if (!SettingsRuntime?.createDockSettingsRuntime || !DockRuntime?.createWebDockRuntime || !FaviconRuntime?.createDockFaviconRuntime || !SettingsPreview?.createDockSettingsPreview || !SettingsViewMarkup?.createDockSettingsViewMarkup || !SettingsBackupSection?.createDockSettingsBackupSection || !SettingsView?.createDockSettingsView || !AddDialog?.createDockAddDialog || !AddDialogView || !SearchDialog?.createDockSearchDialog || !DialogRuntime?.createDockDialogRuntime || !aiSettingsClient?.requestV2AiSettingsGet || !aiSettingsClient?.requestV2AiSettingsSave || !aiSettingsClient?.requestV2AiTestConnection || !aiSettingsClient?.requestAiOriginPermissionSafe) {
            throw new Error("Web Dock composition modules are unavailable");
          }
          let controller;
          let settingsRuntime;
          let faviconRuntime;
          let settingsPreview;
          let settingsView;
          let addDialog;
          let searchDialog;
          let dialogRuntime;
          let settingsMarkup;
          let compositionDestroyed = false;
          const config = () => controller.getConfig();
          const authority = () => controller.authority();
          const core = () => controller.getCore();
          const epoch = () => core()?.getEpoch?.() || 0;
          const current = (generation = epoch()) => controller?.isCurrent(generation) === true;
          const getDialog = () => settingsView?.getDialog?.() || null;
          const getSection = () => settingsView?.getSection?.() || "activation";
          const render = () => core()?.renderView?.();
          const updateHostRecovery = () => core()?.updateHostRecovery?.();
          const recoverHost = () => {
            if (!current() || !config().enabled && !ports.overlayVisible?.()) return void 0;
            return ports.recoverHost?.();
          };
          const projectZoom = (zoom) => {
            const shell = ports.getEls?.()?.webDockShell;
            if (shell) shell.style.setProperty("--dock-page-zoom-inverse", String(1 / zoom));
            core()?.scheduleMeasure?.();
          };
          const showControlError = (element, message) => settingsView?.showError?.(element, message);
          function refreshLanguage() {
            const root = ports.getRoot?.();
            if (root?.querySelectorAll) {
              root.querySelectorAll("[data-i18n]").forEach((node) => {
                const value = ports.translate?.(node.dataset.i18n, {});
                if (value && value !== node.dataset.i18n) node.textContent = value;
              });
              for (const [selector, attribute, datasetKey] of [
                ["[data-i18n-aria-label]", "aria-label", "i18nAriaLabel"],
                ["[data-i18n-title]", "title", "i18nTitle"],
                ["[data-i18n-placeholder]", "placeholder", "i18nPlaceholder"]
              ]) {
                root.querySelectorAll(selector).forEach((node) => {
                  const value = ports.translate?.(node.dataset[datasetKey], {});
                  if (value && value !== node.dataset[datasetKey]) node.setAttribute(attribute, value);
                });
              }
              const panel = root.getElementById?.("panel");
              if (panel) panel.dataset.stickyLabel = ports.translate?.("overlay.sticky", {}) || panel.dataset.stickyLabel;
            }
            settingsView?.refreshLanguage?.();
            addDialog?.refreshLanguage?.();
            searchDialog?.refreshLanguage?.();
            core()?.closeContextMenu?.();
            render();
            return true;
          }
          controller = createWebDockController({
            send: ports.send,
            normalizeConfig: dock.normalizeDockConfig,
            normalizeUrl: dock.normalizeDockUrl,
            defaultConfig: dock.DEFAULT_DOCK_CONFIG,
            instanceCurrent: ports.instanceCurrent,
            projectConfig,
            removeSiteFromConfig: dock.removeDockSite,
            animateRemoval: ports.animateRemoval,
            showToast: ports.showToast,
            showControlError,
            onReorderError: (message) => {
              settingsView?.setSettingsError?.(message);
              showControlError(getDialog()?.querySelector("#dockSettingsError"), message);
            },
            storageKey: dock.DOCK_STORAGE_KEY,
            customIconsKey: limits.customIconsKey,
            settingsRuntime: () => settingsRuntime,
            refreshCustomIcons: (value) => faviconRuntime?.refreshCustomIcons?.(value),
            projectZoom,
            toggleDockSite: ports.toggleDockSite,
            normalizeIconUrl: dock.normalizeDockIconUrl,
            hostOf: ui.hostOf,
            translate: ports.translate,
            refreshLanguage,
            runtimeFactory: DockRuntime.createWebDockRuntime,
            bridge: {
              env: {
                document: env.document,
                window: env.window,
                screen: env.screen,
                MutationObserver: env.MutationObserver,
                setTimeoutFn: env.setTimeoutFn,
                clearTimeoutFn: env.clearTimeoutFn,
                requestFrame: env.requestFrame,
                cancelFrame: env.cancelFrame,
                now: env.now
              },
              getHost: ports.getHost,
              getRoot: ports.getRoot,
              getEls: ports.getEls,
              instanceCurrent: ports.instanceCurrent,
              overlayVisible: ports.overlayVisible,
              stylesReady: ports.stylesReady,
              needsHostRecovery: () => ports.overlayVisible?.() || config().enabled,
              recoverHost,
              onRecoveryCircuitOpen: () => faviconRuntime?.clearTimers?.() ?? ports.onRecoveryCircuitOpen?.(),
              render,
              readStorage: (key) => env.chrome.storage.sync.get(key),
              loadCustomIcons: () => faviconRuntime?.loadCustomIcons?.(),
              getAnimatedItems: ports.getAnimatedItems,
              getItems: ports.getItems,
              getShell: () => ports.getEls?.()?.webDockShell || null,
              getTrack: () => ports.getEls?.()?.webDockTrack || null,
              itemMarkup: (site, options) => ports.dockItemMarkup?.(site, options, faviconRuntime),
              applyAppearance: (shell, appearance) => settingsMarkup?.applyDockTileAppearanceVariables?.(shell, appearance),
              resetFavicons: () => faviconRuntime?.reset?.(),
              syncFavicons: () => faviconRuntime?.syncStates?.(),
              bindFavicons: (track) => faviconRuntime?.bindElements?.(track),
              resolveFavicons: () => faviconRuntime?.resolve?.(),
              openEditor: (item, site) => dialogRuntime?.openAdd?.(item, site),
              openAdd: (item) => dialogRuntime?.openAdd?.(item),
              openSearch: (item) => dialogRuntime?.openSearch?.(item),
              openSettings: (item) => dialogRuntime?.openSettings?.("web-dock", item),
              toggleRecentlyClosed: () => ports.toggleRecentlyClosed?.(),
              closeRecentlyClosed: () => ports.closeRecentlyClosed?.(),
              settleForTabSwitch: ports.settleForTabSwitch,
              translate: ports.translate,
              motion: {
                reorderConfig: dock.reorderDockSitesByUrls,
                reducedMotion: ports.prefersReducedMotion,
                computeTargets: dock.computeDockTargets,
                computeTooltipPlacement: dock.computeDockTooltipPlacement,
                projectPointer: dock.projectDockPointerX,
                stepSpring: dock.stepDockSpring,
                dragThreshold: limits.dragThreshold,
                dragSettleMs: limits.dragSettleMs,
                tooltipGutter: limits.tooltipGutter
              }
            }
          });
          const responseMessage = controller.responseMessage;
          settingsRuntime = SettingsRuntime.createDockSettingsRuntime({
            send: ports.send,
            normalizeConfig: dock.normalizeDockConfig,
            normalizeColor: dock.normalizeDockColor,
            motionLimits: dock.MOTION_LIMITS,
            appearanceLimits: dock.APPEARANCE_LIMITS,
            defaultConfig: dock.DEFAULT_DOCK_CONFIG,
            context: () => ({
              config: config(),
              configRevision: authority().revision,
              generation: epoch(),
              mutationSequence: authority().sequence,
              settingsOpen: Boolean(getDialog() && getSection() === "web-dock")
            }),
            beginMutation: controller.beginMutation,
            applyConfig: controller.applyConfig,
            applyExternalConfig: (value, options = {}) => {
              settingsPreview?.stop?.();
              core()?.cancelInitialization?.();
              controller.applyExternalConfig(value, options);
            },
            showToast: ports.showToast,
            showError: (kind, response, fallback) => {
              const message = responseMessage(response, fallback);
              if (kind === "settings") settingsView?.setSettingsError?.(message);
              const selector = kind === "settings" ? "#dockSettingsError" : "#dockMotionError";
              showControlError(getDialog()?.querySelector(selector), message);
            },
            localWriteTimeoutMs: limits.localWriteTimeoutMs
          });
          settingsRuntime.start(config());
          faviconRuntime = FaviconRuntime.createDockFaviconRuntime({
            chrome: env.chrome,
            document: env.document,
            send: ports.send,
            normalizeDockUrl: dock.normalizeDockUrl,
            safeEphemeralDockIconUrl: dock.safeEphemeralDockIconUrl,
            normalizeDockIconText: dock.normalizeDockIconText,
            serializedByteLength: ui.serializedByteLength,
            compareDockIconCandidates: dock.compareDockIconCandidates,
            requiredDockFaviconPixels: dock.requiredDockFaviconPixels,
            selectDockFaviconSize: dock.selectDockFaviconSize,
            isFinalDockFaviconCandidate: dock.isFinalDockFaviconCandidate,
            isCurrentWebDockLifecycle: current,
            getWebDockItems: ports.getItems,
            syncDockSettingsFaviconState: ports.syncSettingsFaviconState,
            WebDockCatalog: deps.catalog,
            getDevicePixelRatio: ports.devicePixelRatio,
            customIconsKey: limits.customIconsKey,
            customIconMaxDataUrlLength: limits.customIconMaxDataUrlLength,
            context: () => ({
              config: config(),
              revision: authority().revision,
              generation: epoch(),
              recoveryCircuitOpen: core()?.inspect?.().circuitOpen || false,
              settingsDialog: getDialog(),
              settingsSection: getSection()
            }),
            onCustomIconsChanged: () => {
              if (current() && config().enabled && config().sites.length) render();
              else if (getDialog() && getSection() === "web-dock") settingsView?.render?.();
            }
          });
          settingsPreview = SettingsPreview.createDockSettingsPreview({
            context: () => ({
              dialog: getDialog(),
              section: getSection(),
              visualDraft: settingsRuntime.getVisualDraft(),
              config: config()
            }),
            computeTargets: dock.computeDockTargets,
            stepSpring: dock.stepDockSpring,
            createSpringState: ports.createSpringState,
            writeTransform: ports.writeTransform,
            prefersReducedMotion: ports.prefersReducedMotion,
            normalizeColor: dock.normalizeDockColor,
            clearanceGap: limits.previewClearanceGap,
            bottomGap: limits.previewBottomGap
          });
          const initializePreview = () => settingsPreview.initialize(getDialog()?.querySelector("#dockMotionPreview"));
          settingsMarkup = SettingsViewMarkup.createDockSettingsViewMarkup({
            escapeHtml: ui.escapeHtml,
            normalizeDockColor: dock.normalizeDockColor,
            dockFaviconRuntime: faviconRuntime,
            dockSettingsFaviconMarkup: (site) => ports.dockSettingsFaviconMarkup?.(site, faviconRuntime),
            translate: ports.translate
          });
          settingsView = SettingsView.createDockSettingsView({
            document: env.document,
            context: () => ({ root: ports.getRoot?.(), config: config(), settingsButton: ports.getEls?.()?.btnSettings }),
            settingsRuntime,
            activationRuntime: deps.activationRuntime,
            escapeHtml: ui.escapeHtml,
            normalizeDockUrl: dock.normalizeDockUrl,
            normalizeDockColor: dock.normalizeDockColor,
            MOTION_LIMITS: dock.MOTION_LIMITS,
            APPEARANCE_LIMITS: dock.APPEARANCE_LIMITS,
            DOCK_PREVIEW_FALLBACK_SITES: limits.previewFallbackSites,
            moveDockSiteUrls: dock.moveDockSiteUrls,
            reorderDockSitesByUrls: dock.reorderDockSitesByUrls,
            dockSettingsFaviconMarkup: (site) => ports.dockSettingsFaviconMarkup?.(site, faviconRuntime),
            bindDockSettingsFaviconFallbacks: ports.bindSettingsFaviconFallbacks,
            bindDockFaviconElements: faviconRuntime.bindElements,
            dockItemMarkup: (site, options) => ports.dockItemMarkup?.(site, options, faviconRuntime),
            stopDockPreviewAnimation: settingsPreview.stop,
            initializeDockPreviewAnimation: initializePreview,
            renderDockMotionPreview: settingsPreview.render,
            resetDockMotionSettings: () => settingsRuntime.resetVisual(),
            previewRuntime: settingsPreview,
            persistWebDockSite: controller.persistSite,
            removeDockSite: controller.removeSite,
            persistDockSiteOrder: controller.reorderSites,
            setOptimisticConfig: controller.setOptimisticConfig,
            renderWebDock: render,
            applyWebDockConfig: controller.applyConfig,
            dockResponseMessage: responseMessage,
            closeAllMenus: ports.closeAllMenus,
            syncHostPointerInteractivity: ports.syncHost,
            setWebDockExpanded: (value, options) => core()?.setExpanded?.(value, options),
            renderActivationPanel: ports.renderActivation,
            refreshLicenseState: ports.refreshLicense,
            closePurchasePreview: ports.closePurchasePreview,
            closeLicenseDeactivationDialog: ports.closeDeactivation,
            ensureHost: ports.ensureHost,
            queueDockVisualPersistence: settingsRuntime.queueVisual,
            flushDockVisualPersistence: settingsRuntime.flushVisual,
            clearBehaviorData: () => requestV2BehaviorClear(ports.send, ports.translate),
            clearAiData: () => requestV2DataControl(ports.send, "V2_AI_DATA_CLEAR"),
            clearAllData: () => requestV2DataControl(ports.send, "V2_TASK_DATA_CLEAR"),
            exportV2Data: () => requestV2DataControl(ports.send, "V2_DATA_EXPORT"),
            previewV2Import: (backup) => ports.send({ type: "V2_DATA_IMPORT_PREVIEW", backup }),
            importV2Data: (backup, previewToken) => ports.send({
              type: "V2_DATA_IMPORT",
              backup,
              previewToken,
              confirmed: true
            }),
            createDockSettingsBackupSection: SettingsBackupSection.createDockSettingsBackupSection,
            confirmBehaviorClear: (message) => env.window?.confirm?.(message) === true,
            loadAiSettings: () => aiSettingsClient.requestV2AiSettingsGet(ports.send),
            saveAiSettings: (envelope) => aiSettingsClient.requestV2AiSettingsSave(ports.send, envelope),
            testAiConnection: (envelope) => aiSettingsClient.requestV2AiTestConnection(ports.send, envelope),
            requestAiOriginPermission: (baseUrl) => aiSettingsClient.requestAiOriginPermissionSafe(ports, baseUrl),
            loadLanguagePreference: ports.loadLanguagePreference,
            saveLanguagePreference: ports.saveLanguagePreference,
            translate: ports.translate,
            markup: settingsMarkup
          });
          addDialog = AddDialog.createDockAddDialog({
            chrome: env.chrome,
            document: env.document,
            FileReader: env.FileReader,
            Image: env.Image,
            location: env.location,
            WebDockCatalog: deps.catalog,
            view: AddDialogView,
            send: ports.send,
            showToast: ports.showToast,
            escapeHtml: ui.escapeHtml,
            hostOf: ui.hostOf,
            domainColor: ui.domainColor,
            letterOf: ui.letterOf,
            serializedByteLength: ui.serializedByteLength,
            normalizeDockUrl: dock.normalizeDockUrl,
            normalizeDockIconText: dock.normalizeDockIconText,
            normalizeDockTextIconColor: dock.normalizeDockTextIconColor,
            textIconColors: dock.DOCK_TEXT_ICON_COLORS,
            dockResponseMessage: responseMessage,
            builtInWebDockIcon: faviconRuntime.builtInIcon,
            chromeDockFaviconUrl: faviconRuntime.chromeUrl,
            persistWebDockSite: controller.persistSite,
            collectPageIconCandidates: ports.collectPageIconCandidates,
            setWebDockCustomIcon: faviconRuntime.setCustomIcon,
            removeWebDockCustomIcon: faviconRuntime.removeCustomIcon,
            getWebDockCustomIcon: faviconRuntime.getCustomIcon,
            context: () => {
              ports.ensureHost?.();
              return { root: ports.getRoot?.(), config: config() };
            },
            closeOthers: () => {
            },
            syncHost: ports.syncHost,
            setExpanded: (value, options) => core()?.setExpanded?.(value, options),
            uploadMaxBytes: limits.uploadMaxBytes,
            customIconMaxLength: limits.customIconMaxDataUrlLength,
            translate: ports.translate
          });
          searchDialog = SearchDialog.createDockSearchDialog({
            chrome: env.chrome,
            document: env.document,
            send: ports.send,
            escapeHtml: ui.escapeHtml,
            showToast: ports.showToast,
            translate: ports.translate,
            context: () => {
              ports.ensureHost?.();
              return { root: ports.getRoot?.(), searchButton: ports.getEls?.()?.webDockSearch || null };
            },
            closeOthers: () => {
            },
            syncHost: ports.syncHost,
            setExpanded: (value, options) => core()?.setExpanded?.(value, options)
          });
          dialogRuntime = DialogRuntime.createDockDialogRuntime({
            settings: settingsView,
            add: addDialog,
            search: searchDialog,
            activation: deps.activationRuntime,
            closeContextMenu: () => core()?.closeContextMenu?.(),
            closeMenus: () => {
              if (ports.getRoot?.()) ports.closeAllMenus?.();
            }
          });
          function destroyView() {
            core()?.closeContextMenu?.();
            ports.cancelDrag?.();
            ports.cancelSettlingDrag?.();
            faviconRuntime.reset();
            core()?.stopMotion?.();
            core()?.clearAutoHide?.();
            core()?.resetMotion?.();
            core()?.syncConfigState?.({ ...config(), enabled: false });
            const els = ports.getEls?.() || {};
            if (els.webDockShell) {
              els.webDockShell.hidden = true;
              els.webDockShell.style.pointerEvents = "none";
              els.webDockShell.dataset.expanded = "false";
            }
            core()?.syncInteractivity?.();
            if (els.webDockTrack) els.webDockTrack.innerHTML = "";
            core()?.syncInteractivity?.();
            updateHostRecovery();
          }
          function projectConfig(next, previous, options = {}) {
            if (!current()) return false;
            core()?.cancelInitialization?.();
            if (options.updateConfirmedBase !== false) settingsRuntime.onConfigApplied(next);
            const sitesChanged = JSON.stringify(previous.sites) !== JSON.stringify(next.sites);
            const activeUrls = new Set(next.sites.map((site) => site.url));
            const customIcons = faviconRuntime.getCustomIcons();
            const orphaned = Object.keys(customIcons).filter((url) => !activeUrls.has(url));
            if (orphaned.length) {
              const retained = { ...customIcons };
              orphaned.forEach((url) => {
                delete retained[url];
              });
              faviconRuntime.replaceCustomIcons(retained).catch(() => {
              });
            }
            if (getDialog() && getSection() === "web-dock") {
              if (options.syncVisualDraft === true) settingsRuntime.syncVisualDraft(next);
              else settingsRuntime.ensureVisualDraft();
              if (sitesChanged) settingsView.render();
              else settingsView.syncControls({ syncVisual: options.syncVisualDraft === true, clearError: options.clearVisualError === true });
            }
            if (ports.overlayVisible?.() && ports.getEls?.()?.body) {
              ports.renderTabs?.();
              ports.updateCount?.();
            }
            if (!next.enabled) {
              destroyView();
              return true;
            }
            ports.ensureHost?.();
            core()?.syncConfigState?.(next);
            render();
            updateHostRecovery();
            return true;
          }
          function pause() {
            if (core()?.inspect?.().destroyed || core()?.inspect?.().paused) return false;
            ports.cancelDrag?.();
            ports.cancelSettlingDrag?.();
            controller.pause();
            ports.pauseOverlaySession?.();
            if (!ports.stylesReady?.()) ports.resetCss?.();
            faviconRuntime.clearTimers();
            core()?.cancelInitialization?.();
            core()?.stopMotion?.();
            core()?.clearAutoHide?.();
            return true;
          }
          async function resume() {
            if (core()?.inspect?.().destroyed || !core()?.inspect?.().paused) return false;
            await controller.resume();
            if (controller.snapshot().paused) return false;
            ports.resumeOverlaySession?.();
            const generation = epoch();
            if (!current(generation)) return false;
            if (ports.getHost?.() && ports.getRoot?.() && !ports.stylesReady?.()) ports.preloadCss?.(generation);
            updateHostRecovery();
            return true;
          }
          function destroy() {
            if (compositionDestroyed) return false;
            compositionDestroyed = true;
            const safely = (operation) => {
              try {
                operation();
              } catch (_error) {
              }
            };
            safely(() => controller.setOptimisticConfig(dock.DEFAULT_DOCK_CONFIG));
            safely(destroyView);
            safely(controller.destroy);
            safely(faviconRuntime.destroy);
            safely(settingsRuntime.destroy);
            safely(dialogRuntime.destroy);
            safely(settingsPreview.destroy);
            return true;
          }
          return Object.freeze({
            controller,
            runtime: core(),
            settingsRuntime,
            faviconRuntime,
            settingsPreview,
            settingsView,
            addDialog,
            searchDialog,
            dialogRuntime,
            config,
            authority,
            epoch,
            current,
            responseMessage,
            start: controller.start,
            pause,
            resume,
            destroyView,
            destroy,
            applyConfig: controller.applyConfig,
            handleStorageChange: controller.handleStorageChange,
            applyBroadcast: controller.applyBroadcast,
            refreshZoom: controller.refreshZoom,
            applyZoom: controller.applyZoom,
            render,
            refreshLanguage,
            updateHostRecovery,
            scheduleHostRecovery: () => core()?.scheduleHostRecovery?.(),
            updateFullscreen: () => core()?.updateFullscreen?.()
          });
        }
        return { createWebDockController, createWebDockComposition, requestV2BehaviorClear, requestV2DataControl };
      });
    }
  });

  // src/content/document-dock-lifecycle-runtime.js
  var require_document_dock_lifecycle_runtime = __commonJS({
    "src/content/document-dock-lifecycle-runtime.js"(exports, module) {
      (function(root, factory) {
        const api = factory();
        const commonJs = typeof module === "object" && module.exports;
        if (commonJs) module.exports = api;
        if (typeof window === "object") window.TfaDocumentDockLifecycle = api;
        else if (!commonJs) root.TfaDocumentDockLifecycle = api;
      })(typeof globalThis !== "undefined" ? globalThis : exports, function() {
        "use strict";
        function create(deps = {}) {
          const {
            lifecycle,
            document: document2,
            window: window2,
            current,
            cancelDrag,
            getHost,
            recoverHost,
            updateSuppression,
            startDock
          } = deps;
          if (!lifecycle?.listen || !document2?.addEventListener || !window2?.addEventListener) {
            throw new TypeError("document dock lifecycle dependencies are invalid");
          }
          let started = false;
          function active() {
            return typeof current !== "function" || current();
          }
          function handleFullscreen(property) {
            if (!active()) return;
            const fullscreenElement = document2[property];
            if (fullscreenElement) cancelDrag?.();
            const host = getHost?.();
            if (host && !host.isConnected) {
              recoverHost?.();
              return;
            }
            if (fullscreenElement && fullscreenElement !== document2.documentElement) {
              if (host && host.parentElement !== fullscreenElement) fullscreenElement.appendChild(host);
            } else if (!fullscreenElement && host && host.parentElement !== document2.documentElement) {
              document2.documentElement.appendChild(host);
            }
            updateSuppression?.();
          }
          function startDockSafely() {
            try {
              Promise.resolve(startDock?.()).catch(() => {
              });
            } catch (_) {
            }
          }
          function start() {
            if (started) return false;
            started = true;
            lifecycle.listen(document2, "fullscreenchange", () => handleFullscreen("fullscreenElement"));
            lifecycle.listen(document2, "webkitfullscreenchange", () => handleFullscreen("webkitFullscreenElement"));
            lifecycle.listen(window2, "resize", () => {
              if (active()) updateSuppression?.();
            });
            if (document2.readyState === "loading") {
              lifecycle.listen(document2, "DOMContentLoaded", startDockSafely, { once: true });
            } else {
              startDockSafely();
            }
            return true;
          }
          return Object.freeze({ start });
        }
        return Object.freeze({ create });
      });
    }
  });

  // src/content/content-feature-shared-shell.js
  var require_content_feature_shared_shell = __commonJS({
    "src/content/content-feature-shared-shell.js"(exports, module) {
      (function attachContentFeatureSharedShell(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaContentFeatureSharedShell = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createContentFeatureSharedShellModule() {
        "use strict";
        const FEATURE_NAMES = Object.freeze(["web_dock", "workspace"]);
        const WORKSPACE_PORTS = Object.freeze([
          "overlayVisible",
          "pauseOverlaySession",
          "resumeOverlaySession",
          "renderTabs",
          "updateCount",
          "closeAllMenus"
        ]);
        function createContentFeatureSharedShell(options = {}) {
          const documentRef = options.document;
          const buildHost = options.buildHost;
          const current = typeof options.current === "function" ? options.current : () => true;
          if (!documentRef || typeof buildHost !== "function") {
            throw new Error("shared shell requires document and buildHost");
          }
          const references = /* @__PURE__ */ new Map();
          let context = null;
          let workspacePorts = null;
          let destroyed = false;
          function validFeature(name) {
            return FEATURE_NAMES.includes(String(name || ""));
          }
          function connected(candidate = context) {
            return Boolean(candidate?.host && candidate.host.isConnected !== false);
          }
          function normalizeContext(candidate) {
            if (!candidate?.host || !candidate.root || !candidate.els || typeof candidate.els !== "object") {
              throw new Error("buildHost must return host, root, and els");
            }
            return { host: candidate.host, root: candidate.root, els: candidate.els };
          }
          function removeHost(candidate = context) {
            const host = candidate?.host;
            if (!host) return;
            try {
              if (typeof host.remove === "function") host.remove();
              else host.parentNode?.removeChild?.(host);
            } catch (_error) {
            }
          }
          function ensure() {
            if (destroyed || current() === false) return null;
            if (connected()) return context;
            const previous = context;
            const next = normalizeContext(buildHost({ document: documentRef, previous }));
            context = next;
            if (previous && previous.host !== next.host) removeHost(previous);
            return context;
          }
          function recover() {
            return connected() ? context : ensure();
          }
          function retainFeature(name) {
            const feature = String(name || "");
            if (destroyed || !validFeature(feature)) return null;
            references.set(feature, (references.get(feature) || 0) + 1);
            try {
              return ensure();
            } catch (error) {
              releaseFeature(feature);
              throw error;
            }
          }
          function releaseFeature(name) {
            const feature = String(name || "");
            const count = references.get(feature) || 0;
            if (!count) return false;
            if (count > 1) references.set(feature, count - 1);
            else references.delete(feature);
            if (references.size === 0) {
              removeHost();
              context = null;
            }
            return true;
          }
          function attachWorkspacePorts(ports = {}) {
            if (destroyed || !ports || typeof ports !== "object") return () => false;
            const record = Object.fromEntries(WORKSPACE_PORTS.map((name) => [
              name,
              typeof ports[name] === "function" ? ports[name] : null
            ]));
            workspacePorts = record;
            let attached = true;
            return () => {
              if (!attached || workspacePorts !== record) return false;
              attached = false;
              workspacePorts = null;
              return true;
            };
          }
          function callWorkspacePort(name, ...args) {
            const port = workspacePorts && workspacePorts[name];
            return typeof port === "function" ? port(...args) : void 0;
          }
          const workspaceBridge = Object.freeze({
            overlayVisible: () => Boolean(callWorkspacePort("overlayVisible")),
            pauseOverlaySession: () => callWorkspacePort("pauseOverlaySession"),
            resumeOverlaySession: () => callWorkspacePort("resumeOverlaySession"),
            renderTabs: () => callWorkspacePort("renderTabs"),
            updateCount: () => callWorkspacePort("updateCount"),
            closeAllMenus: () => callWorkspacePort("closeAllMenus")
          });
          function getContext() {
            return context ? { host: context.host, root: context.root, els: context.els } : null;
          }
          function inspect() {
            return {
              destroyed,
              connected: connected(),
              features: Object.fromEntries(references),
              workspaceAttached: Boolean(workspacePorts)
            };
          }
          function attachToOwner(owner) {
            if (destroyed || !owner || owner.featureHost !== true) return false;
            if (owner.sharedShell && owner.sharedShell !== api) return false;
            owner.sharedShell = api;
            return true;
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            references.clear();
            workspacePorts = null;
            removeHost();
            context = null;
            return true;
          }
          const api = Object.freeze({
            ensure,
            recover,
            retainFeature,
            releaseFeature,
            attachWorkspacePorts,
            workspaceBridge,
            getContext,
            attachToOwner,
            inspect,
            destroy
          });
          return api;
        }
        return Object.freeze({ FEATURE_NAMES, WORKSPACE_PORTS, createContentFeatureSharedShell });
      });
    }
  });

  // src/content/web-dock-feature-initializer.js
  var require_web_dock_feature_initializer = __commonJS({
    "src/content/web-dock-feature-initializer.js"(exports, module) {
      (function attachWebDockFeatureInitializer(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaWebDockFeatureInitializer = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createWebDockFeatureInitializerModule() {
        "use strict";
        const REQUIRED_PORTS = Object.freeze([
          "send",
          "instanceCurrent",
          "getHost",
          "getRoot",
          "getEls",
          "overlayVisible",
          "stylesReady",
          "recoverHost",
          "ensureHost",
          "showToast",
          "toggleDockSite",
          "animateRemoval",
          "getItems",
          "getAnimatedItems",
          "dockItemMarkup",
          "dockSettingsFaviconMarkup",
          "bindSettingsFaviconFallbacks",
          "syncSettingsFaviconState",
          "collectPageIconCandidates",
          "devicePixelRatio",
          "createSpringState",
          "writeTransform",
          "prefersReducedMotion",
          "settleForTabSwitch",
          "cancelDrag",
          "cancelSettlingDrag",
          "pauseOverlaySession",
          "resumeOverlaySession",
          "resetCss",
          "preloadCss",
          "renderTabs",
          "updateCount",
          "closeAllMenus",
          "syncHost",
          "renderActivation",
          "refreshLicense",
          "closePurchasePreview",
          "closeDeactivation",
          "toggleRecentlyClosed",
          "closeRecentlyClosed",
          "translate",
          "onRecoveryCircuitOpen"
        ]);
        function requireFunction(value, name) {
          if (typeof value !== "function") throw new Error(`web dock initializer requires ${name}`);
          return value;
        }
        function validateCompositionOptions(value) {
          if (!value || typeof value !== "object") throw new Error("compositionOptions must return an object");
          for (const name of ["env", "dockLogic", "uiLogic", "catalog", "activationRuntime", "limits", "ports"]) {
            if (!value[name] || typeof value[name] !== "object") throw new Error(`web dock composition missing ${name}`);
          }
          const missing = REQUIRED_PORTS.filter((name) => typeof value.ports[name] !== "function");
          if (missing.length) throw new Error(`web dock composition missing ports: ${missing.join(", ")}`);
          if (typeof value.ports.aiHostPermissionAlreadyGranted !== "boolean") {
            throw new Error("web dock composition missing ports: aiHostPermissionAlreadyGranted");
          }
          return value;
        }
        function validateRuntime(value, name, methods) {
          if (!value || typeof value !== "object") throw new Error(`${name} factory returned no runtime`);
          const missing = methods.filter((method) => typeof value[method] !== "function");
          if (missing.length) throw new Error(`${name} runtime missing: ${missing.join(", ")}`);
          return value;
        }
        function createWebDockFeatureInitializer(options = {}) {
          const sharedShell = options.sharedShell;
          if (!sharedShell?.retainFeature || !sharedShell?.releaseFeature || !sharedShell?.workspaceBridge) {
            throw new Error("web dock initializer requires sharedShell");
          }
          const createComposition = requireFunction(
            options.createComposition || options.webDockController?.createWebDockComposition,
            "createComposition"
          );
          const compositionOptions = requireFunction(options.compositionOptions, "compositionOptions");
          const createLifecycle = requireFunction(options.createLifecycle, "createLifecycle");
          const createAuthority = requireFunction(options.createAuthority, "createAuthority");
          const createRecentlyClosed = requireFunction(options.createRecentlyClosed, "createRecentlyClosed");
          const recentlyClosedOptions = requireFunction(options.recentlyClosedOptions, "recentlyClosedOptions");
          const handleIntent = typeof options.handleIntent === "function" ? options.handleIntent : null;
          let lifecycle = null;
          let authority = null;
          let composition = null;
          let recentlyClosed = null;
          let startPromise = null;
          let retained = false;
          let retainedHost = null;
          let destroyed = false;
          let started = false;
          function dependencies() {
            return {
              context: sharedShell.getContext?.(),
              sharedShell,
              workspaceBridge: sharedShell.workspaceBridge,
              lifecycle,
              authority,
              composition,
              getComposition: () => composition,
              getRecentlyClosed: () => recentlyClosed
            };
          }
          async function replayIntent(intent) {
            if (!intent || destroyed || !composition) return false;
            if ((intent.type === "WEB_DOCK_CONFIG_CHANGED" || intent.type === "dock-enabled") && intent.config) {
              return composition.applyBroadcast(intent) === true;
            }
            if (intent.type === "dock-enabled") {
              return composition.render?.() !== false;
            }
            if (intent.type === "WEB_DOCK_RECENTLY_CLOSED_CHANGED") {
              return recentlyClosed?.handleExternalChange?.(intent) === true;
            }
            if (intent.type === "WEB_DOCK_ZOOM_CHANGED") {
              composition.applyZoom(intent.zoom);
              return true;
            }
            if (handleIntent) return await handleIntent(intent, dependencies()) !== false;
            return false;
          }
          function initialize() {
            const shellContext = sharedShell.retainFeature("web_dock");
            if (!shellContext) throw new Error("web dock shared shell is unavailable");
            retained = true;
            retainedHost = shellContext.host || null;
            retainedHost?.removeAttribute?.("data-tfa-web-dock-runtime-ready");
            lifecycle = validateRuntime(createLifecycle({ ...dependencies(), context: shellContext }), "lifecycle", ["start", "destroy"]);
            authority = validateRuntime(createAuthority(dependencies()), "authority", ["start", "destroy"]);
            lifecycle.start();
            authority.start();
            const deps = validateCompositionOptions(compositionOptions(dependencies()));
            composition = validateRuntime(createComposition(deps), "composition", [
              "start",
              "destroy",
              "applyBroadcast",
              "applyZoom"
            ]);
            recentlyClosed = validateRuntime(
              createRecentlyClosed(recentlyClosedOptions(dependencies())),
              "recently closed",
              ["start", "destroy"]
            );
            recentlyClosed.start();
            startPromise = Promise.resolve(composition.start()).then((result) => {
              if (destroyed) return false;
              started = result !== false;
              if (started) retainedHost?.setAttribute?.("data-tfa-web-dock-runtime-ready", "true");
              return started;
            });
            return startPromise;
          }
          async function start(intent) {
            if (destroyed) return false;
            if (!startPromise) {
              try {
                initialize();
              } catch (error) {
                destroy();
                throw error;
              }
            }
            const ready = await startPromise;
            if (!ready || destroyed) return false;
            await replayIntent(intent);
            return true;
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            const safely = (operation) => {
              try {
                operation?.();
              } catch (_error) {
              }
            };
            safely(() => recentlyClosed?.destroy());
            safely(() => composition?.destroy());
            safely(() => authority?.destroy());
            safely(() => lifecycle?.destroy());
            safely(() => retainedHost?.removeAttribute?.("data-tfa-web-dock-runtime-ready"));
            if (retained) safely(() => sharedShell.releaseFeature("web_dock"));
            retained = false;
            retainedHost = null;
            started = false;
            return true;
          }
          function inspect() {
            return Object.freeze({
              destroyed,
              started,
              loading: Boolean(startPromise && !started),
              hasComposition: Boolean(composition),
              hasRecentlyClosed: Boolean(recentlyClosed)
            });
          }
          return Object.freeze({ start, destroy, replayIntent, inspect });
        }
        return Object.freeze({ REQUIRED_PORTS, validateCompositionOptions, createWebDockFeatureInitializer });
      });
    }
  });

  // src/content/web-dock-feature-environment.js
  var require_web_dock_feature_environment = __commonJS({
    "src/content/web-dock-feature-environment.js"(exports, module) {
      (function attachWebDockFeatureEnvironment(root, factory) {
        const api = factory();
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaWebDockFeatureEnvironment = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createWebDockFeatureEnvironmentModule() {
        "use strict";
        const REQUIRED_IDS = Object.freeze([
          "overlay",
          "panel",
          "toastContainer",
          "webDockShell",
          "webDockTrigger",
          "webDockTrack",
          "webDockAdd",
          "webDockSearch",
          "webDockSettings",
          "webDockRecent",
          "webDockRecentPanel",
          "webDockRecentStatus",
          "webDockRecentList",
          "webDockRecentCount"
        ]);
        const LIMITS = Object.freeze({
          customIconsKey: "tfa_web_dock_custom_icons_v1",
          customIconMaxDataUrlLength: 60 * 1024,
          uploadMaxBytes: 512 * 1024,
          localWriteTimeoutMs: 3e3,
          previewClearanceGap: 8,
          previewBottomGap: 18,
          previewFallbackSites: Object.freeze([
            { id: "preview-google", title: "Google", url: "https://www.google.com/" },
            { id: "preview-youtube", title: "YouTube", url: "https://www.youtube.com/" },
            { id: "preview-github", title: "GitHub", url: "https://github.com/" },
            { id: "preview-chatgpt", title: "ChatGPT", url: "https://chatgpt.com/" },
            { id: "preview-taobao", title: "Taobao", url: "https://www.taobao.com/" }
          ]),
          dragThreshold: 6,
          dragSettleMs: 210,
          tooltipGutter: 8
        });
        function requireApi(value, name, method) {
          if (!value || typeof value[method] !== "function") throw new Error(`web dock environment requires ${name}.${method}`);
          return value;
        }
        function createWebDockFeatureEnvironment(options = {}) {
          const windowRef = options.window;
          const documentRef = options.document;
          const chromeApi = options.chrome;
          const owner = options.owner;
          const identity = options.identity || {};
          const modules = options.modules || {};
          if (!windowRef || !documentRef || !chromeApi?.runtime || owner?.featureHost !== true) {
            throw new Error("web dock environment requires active bootstrap owner");
          }
          const ShellModule = requireApi(modules.sharedShell, "sharedShell", "createContentFeatureSharedShell");
          const InitializerModule = requireApi(modules.initializer, "initializer", "createWebDockFeatureInitializer");
          const OverlayRuntime = requireApi(modules.overlayRuntime, "overlayRuntime", "createOverlayRuntime");
          const OverlayTemplate = requireApi(modules.overlayTemplate, "overlayTemplate", "buildOverlay");
          const ThemeRuntime = requireApi(modules.themeRuntime, "themeRuntime", "create");
          const LifecycleRuntime = requireApi(modules.lifecycleRuntime, "lifecycleRuntime", "createLifecycleRuntime");
          const AuthorityRuntime = requireApi(modules.authorityRuntime, "authorityRuntime", "createDockAuthorityRuntime");
          const RecentRuntime = requireApi(modules.recentRuntime, "recentRuntime", "createWebDockRecentlyClosedRuntime");
          const DocumentDockLifecycle = requireApi(modules.documentDockLifecycle, "documentDockLifecycle", "create");
          const ActivationRuntime = requireApi(modules.activationRuntime, "activationRuntime", "create");
          const TabActions = requireApi(modules.tabActions, "tabActions", "createTabActions");
          const WebDockController = requireApi(modules.webDockController, "webDockController", "createWebDockComposition");
          const DockLogic = modules.dockLogic;
          const Ui = modules.uiLogic;
          const Catalog = modules.catalog;
          const I18n = modules.i18n;
          if (!DockLogic || !Ui || !Catalog || !I18n?.createTranslator) throw new Error("web dock environment shared logic unavailable");
          const extensionId = chromeApi.runtime.id || "unknown-extension";
          const instanceId = String(owner.instanceId || "bootstrap");
          const hostId = `tfa-host-${extensionId}`;
          const send = (message) => new Promise((resolve) => {
            try {
              chromeApi.runtime.sendMessage(message, (response) => resolve(response || { ok: false }));
            } catch (_error) {
              resolve({ ok: false });
            }
          });
          const collectElements = (root) => Object.fromEntries(REQUIRED_IDS.map((id) => [id, root.getElementById(id)]));
          let authority = null;
          let initializer = null;
          let activation = null;
          let tabActions = null;
          const overlayRuntime = OverlayRuntime.createOverlayRuntime({
            document: documentRef,
            window: windowRef,
            hostId,
            owner: extensionId,
            instanceId,
            hostStyle: "all:initial;position:fixed;inset:0;z-index:2147483647;pointer-events:none;",
            requiredIds: REQUIRED_IDS,
            collectElements,
            buildOverlay(root) {
              OverlayTemplate.buildOverlay({ root, version: identity.version || "", translate: (key, params) => translator(key, params) });
              return { elements: collectElements(root) };
            },
            fetchStyles: async () => {
              const response = await (options.fetch || windowRef.fetch.bind(windowRef))(chromeApi.runtime.getURL("styles.css"));
              if (response && typeof response.ok === "boolean" && !response.ok) return "";
              return response.text();
            },
            onHost({ host }) {
              authority?.markOwnerHost?.(host, { version: identity.version, fingerprint: identity.fingerprint });
            },
            onCssReady() {
              const state = initializer?.inspect?.();
              if (state?.started) initializer.replayIntent({ type: "dock-enabled" });
            }
          });
          let sharedShell = owner.sharedShell;
          if (!sharedShell?.retainFeature) {
            sharedShell = ShellModule.createContentFeatureSharedShell({
              document: documentRef,
              current: () => owner.state === "active" && windowRef.__TFA_OWNER__ === owner,
              buildHost() {
                overlayRuntime.ensureHost();
                const current = overlayRuntime.getContext();
                void overlayRuntime.preloadCss();
                return { host: current.host, root: current.root, els: current.elements };
              }
            });
            if (!sharedShell.attachToOwner(owner)) throw new Error("web dock shared shell owner conflict");
          }
          const context = () => sharedShell.getContext?.() || null;
          const composition = () => environmentState.getComposition?.() || null;
          const recent = () => environmentState.getRecentlyClosed?.() || null;
          const environmentState = { getComposition: null, getRecentlyClosed: null };
          const runtime = () => composition()?.runtime || null;
          const config = () => composition()?.config?.() || DockLogic.DEFAULT_DOCK_CONFIG;
          const items = () => Array.from(context()?.els?.webDockTrack?.querySelectorAll?.(".web-dock-item") || []);
          const animatedItems = () => [
            context()?.els?.webDockAdd,
            context()?.els?.webDockSearch,
            ...items(),
            context()?.els?.webDockSettings,
            context()?.els?.webDockRecent
          ].filter(Boolean);
          const languageStorageKey = I18n.LANGUAGE_STORAGE_KEY || "tfa_language";
          let languagePreference = "system";
          let translator = I18n.createTranslator(I18n.resolveLanguage({
            browserLanguage: chromeApi.i18n?.getUILanguage?.(),
            navigatorLanguage: windowRef.navigator?.language
          }));
          let themeRuntime = null;
          let appearanceEpoch = 0, languageRevision = 0, appearanceDestroyed = false;
          let appearanceReady = Promise.resolve(true);
          function normalizeLanguagePreference(value) {
            const raw = String(value || "").trim().toLowerCase();
            if (!raw || raw === "system") return "system";
            if (raw.startsWith("zh")) return (I18n.normalizeLanguage?.(value) || "zh") === "zh" ? "zh-CN" : "system";
            if (raw.startsWith("en")) return I18n.normalizeLanguage?.(value) || "en";
            return "system";
          }
          function applyLanguagePreference(value) {
            languagePreference = normalizeLanguagePreference(value);
            translator = I18n.createTranslator(I18n.resolveLanguage({
              manual: languagePreference,
              browserLanguage: chromeApi.i18n?.getUILanguage?.(),
              navigatorLanguage: windowRef.navigator?.language
            }));
            return languagePreference;
          }
          async function loadLanguagePreference() {
            const epoch = appearanceEpoch, revision = languageRevision;
            let saved = {};
            try {
              saved = await chromeApi.storage?.sync?.get?.(languageStorageKey) || {};
            } catch (_error) {
              saved = {};
            }
            if (appearanceDestroyed || epoch !== appearanceEpoch || revision !== languageRevision) return languagePreference;
            return applyLanguagePreference(saved[languageStorageKey]);
          }
          async function saveLanguagePreference(value) {
            const preference = normalizeLanguagePreference(value);
            languageRevision += 1;
            applyLanguagePreference(preference);
            composition()?.refreshLanguage?.();
            recent()?.refreshLanguage?.();
            await chromeApi.storage?.sync?.set?.({ [languageStorageKey]: preference });
            return preference;
          }
          function handleAppearanceStorageChange(changes, areaName) {
            if (areaName !== "sync") return;
            if (changes?.[languageStorageKey]) {
              languageRevision += 1;
              applyLanguagePreference(changes[languageStorageKey].newValue);
              composition()?.refreshLanguage?.();
              recent()?.refreshLanguage?.();
            }
          }
          function ensureHost() {
            const result = sharedShell.ensure();
            if (result?.host) themeRuntime?.apply?.();
            if (result) void overlayRuntime.preloadCss();
            return result?.host || null;
          }
          function recoverHost() {
            const before = context()?.host;
            const result = sharedShell.recover();
            if (result?.host) themeRuntime?.apply?.();
            if (result?.host && result.host !== before) void overlayRuntime.preloadCss();
            return result?.host || null;
          }
          function showToast(message, type = "info", duration = 2200) {
            const container = context()?.els?.toastContainer;
            if (!container) return false;
            const toast = documentRef.createElement("div");
            toast.className = `toast toast-${type}`;
            toast.textContent = String(message || "");
            container.appendChild(toast);
            windowRef.requestAnimationFrame(() => toast.classList.add("show"));
            windowRef.setTimeout(() => {
              toast.classList.add("hide");
              toast.classList.remove("show");
              windowRef.setTimeout(() => toast.remove(), 250);
            }, duration);
            return true;
          }
          function faviconRuntime() {
            return composition()?.faviconRuntime || null;
          }
          function dockItemMarkup(site, renderOptions = {}, favicon = faviconRuntime()) {
            const source = favicon?.source?.(site) || "";
            const state = favicon?.getState?.(site.url);
            const text = site.iconMode === "text" ? DockLogic.normalizeDockIconText(site.iconText) : "";
            const fallback = text || (Ui.hostOf(site.url) || site.title || "?").trim().charAt(0).toUpperCase() || "?";
            const fallbackColor = text ? DockLogic.normalizeDockTextIconColor?.(site.iconColor) || Ui.domainColor(site.url) : Ui.domainColor(site.url);
            const image = `<img class="web-dock-favicon"${source ? ` src="${Ui.escapeHtml(source)}"` : " hidden"} alt="" draggable="false" referrerpolicy="no-referrer">`;
            const preview = renderOptions.preview === true ? ' data-dock-preview-site="true" tabindex="-1"' : "";
            return `<button class="web-dock-item" type="button" data-dock-site-id="${Ui.escapeHtml(site.id)}" data-dock-site-url="${Ui.escapeHtml(site.url)}" data-icon-mode="${text ? "text" : "auto"}" data-icon-text-length="${text.length}" data-favicon-state="${Ui.escapeHtml(state?.status || "pending")}" data-favicon-quality="${Ui.escapeHtml(state?.quality || "pending")}"${preview} aria-label="${Ui.escapeHtml(site.title)}"><span class="web-dock-icon-wrap"><span class="dock-favicon-fallback" aria-hidden="true"${source ? " hidden" : ""} style="background:${fallbackColor}">${Ui.escapeHtml(fallback)}</span>${image}</span><span class="web-dock-label" role="tooltip"><span class="web-dock-label-text">${Ui.escapeHtml(site.title)}</span></span></button>`;
          }
          function dockSettingsFaviconMarkup(site, favicon = faviconRuntime()) {
            const state = favicon?.getState?.(site.url);
            const source = state?.committedUrl || favicon?.source?.(site) || "";
            const fallback = site.iconMode === "text" && DockLogic.normalizeDockIconText(site.iconText) ? DockLogic.normalizeDockIconText(site.iconText) : (Ui.hostOf(site.url) || site.title || "?").trim().charAt(0).toUpperCase() || "?";
            const fallbackColor = site.iconMode === "text" ? DockLogic.normalizeDockTextIconColor?.(site.iconColor) || Ui.domainColor(site.url) : Ui.domainColor(site.url);
            return `<span class="dock-settings-favicon-wrap"><span class="dock-settings-favicon-fallback" aria-hidden="true"${source ? " hidden" : ""} style="background:${fallbackColor}">${Ui.escapeHtml(fallback)}</span>${source ? `<img class="dock-settings-favicon" src="${Ui.escapeHtml(source)}" alt="" draggable="false" referrerpolicy="no-referrer">` : ""}</span>`;
          }
          function bindSettingsFaviconFallbacks() {
            const dialog = composition()?.settingsView?.getDialog?.();
            dialog?.querySelectorAll?.("img.dock-settings-favicon").forEach((image) => {
              bindSettingsFaviconElement(image);
            });
          }
          function setSettingsFaviconVisibility(image, fallback, visible) {
            image.hidden = !visible;
            image.style.display = visible ? "" : "none";
            fallback.hidden = visible;
            fallback.style.display = visible ? "none" : "";
          }
          function bindSettingsFaviconElement(image) {
            if (!image || image.dataset.dockFallbackBound === "true") return;
            const fallback = image.closest(".dock-settings-favicon-wrap")?.querySelector(".dock-settings-favicon-fallback");
            if (!fallback) return;
            image.dataset.dockFallbackBound = "true";
            image.addEventListener("load", () => setSettingsFaviconVisibility(image, fallback, true));
            image.addEventListener("error", () => setSettingsFaviconVisibility(image, fallback, false));
            if (image.complete) setSettingsFaviconVisibility(image, fallback, image.naturalWidth > 0);
          }
          function syncSettingsFaviconState(state) {
            const view = composition()?.settingsView;
            const dialog = view?.getDialog?.();
            if (!dialog || view?.getSection?.() !== "web-dock") return;
            const row = Array.from(dialog.querySelectorAll(".dock-site-row")).find(
              (candidate) => candidate.dataset.dockSiteUrl === state.url
            );
            const wrapper = row?.querySelector(".dock-settings-favicon-wrap");
            const fallback = wrapper?.querySelector(".dock-settings-favicon-fallback");
            if (!row || !wrapper || !fallback) return;
            row.dataset.faviconQuality = state.quality;
            let image = wrapper.querySelector("img.dock-settings-favicon");
            if (!state.committedUrl) {
              if (image) {
                image.removeAttribute("src");
                setSettingsFaviconVisibility(image, fallback, false);
              } else {
                fallback.hidden = false;
                fallback.style.display = "";
              }
              return;
            }
            if (!image) {
              image = documentRef.createElement("img");
              image.className = "dock-settings-favicon";
              image.setAttribute("alt", "");
              image.setAttribute("draggable", "false");
              image.setAttribute("referrerpolicy", "no-referrer");
              wrapper.appendChild(image);
              bindSettingsFaviconElement(image);
            }
            if (image.getAttribute("src") !== state.committedUrl) image.setAttribute("src", state.committedUrl);
            setSettingsFaviconVisibility(image, fallback, true);
            if (image.complete) setSettingsFaviconVisibility(image, fallback, image.naturalWidth > 0);
          }
          function collectPageIconCandidates() {
            const result = [];
            for (const link of Array.from(documentRef.querySelectorAll("link")).slice(0, 32)) {
              const rel = String(link.getAttribute("rel") || "").toLowerCase().split(/\s+/);
              if (!rel.some((value) => value === "icon" || value === "shortcut" || value === "apple-touch-icon")) continue;
              const url = DockLogic.safeEphemeralDockIconUrl(link.getAttribute("href") || "", windowRef.location.href);
              if (!url) continue;
              const sizes = String(link.getAttribute("sizes") || "");
              const declaredSize = Math.max(0, ...Array.from(sizes.matchAll(/(\d+)x(\d+)/g), (match) => Math.min(Number(match[1]), Number(match[2]))));
              const vector = String(link.getAttribute("type") || "").toLowerCase() === "image/svg+xml" || /\.svg(?:$|[?#])/i.test(url);
              result.push({ url, source: vector ? "page-svg" : rel.includes("apple-touch-icon") ? "page-touch" : "page-icon", vector, declaredSize, verified: true });
            }
            return result.sort(DockLogic.compareDockIconCandidates).slice(0, 12);
          }
          function animateRemoval(item, nextConfig) {
            if (!item?.isConnected || !context()?.els?.webDockTrack) {
              composition()?.controller?.setOptimisticConfig?.(nextConfig);
              composition()?.render?.();
              return;
            }
            const oldItems = items();
            const previousLeft = new Map(oldItems.map((candidate) => [
              candidate.dataset.dockSiteUrl,
              candidate.getBoundingClientRect().left
            ]));
            const icon = item.querySelector(".web-dock-icon-wrap");
            if (icon) {
              const rect = icon.getBoundingClientRect();
              const ghost = icon.cloneNode(true);
              ghost.classList.add("web-dock-removal-ghost");
              Object.assign(ghost.style, { left: `${rect.left}px`, top: `${rect.top}px`, width: `${rect.width}px`, height: `${rect.height}px` });
              context().root.appendChild(ghost);
              const animation = ghost.animate?.([
                { opacity: 1, transform: "translate3d(0,0,0) scale(1)" },
                { opacity: 0, transform: "translate3d(0,8px,0) scale(.72)" }
              ], { duration: 170, easing: "cubic-bezier(.22,1,.36,1)" });
              if (animation) animation.finished.catch(() => {
              }).then(() => ghost.remove());
              else ghost.remove();
            }
            composition()?.controller?.setOptimisticConfig?.(nextConfig);
            composition()?.render?.();
            windowRef.requestAnimationFrame(() => {
              for (const candidate of items()) {
                const oldLeft = previousLeft.get(candidate.dataset.dockSiteUrl);
                const delta = Number.isFinite(oldLeft) ? oldLeft - candidate.getBoundingClientRect().left : 0;
                const wrap = candidate.querySelector(".web-dock-icon-wrap");
                if (delta && wrap?.animate) wrap.animate([
                  { transform: `translate3d(${delta}px,0,0)` },
                  { transform: "translate3d(0,0,0)" }
                ], { duration: 210, easing: "cubic-bezier(.22,1,.36,1)" });
              }
            });
          }
          function settleForTabSwitch() {
            runtime()?.clearAutoHide?.();
            runtime()?.resetMotion?.();
            if (config().autoHide) runtime()?.setExpanded?.(false);
          }
          function syncHost() {
            const shell = context();
            if (!shell?.host) return;
            const modal = Boolean(shell.root.querySelector(".settings-dialog-shell,.dock-add-shell,.dock-search-shell"));
            const recentPanelOpen = shell.root.querySelector("#webDockRecentPanel")?.hidden === false;
            shell.host.style.pointerEvents = sharedShell.workspaceBridge.overlayVisible() || modal || recentPanelOpen ? "auto" : "none";
          }
          function activationRuntime() {
            if (activation) return activation;
            activation = ActivationRuntime.create({
              chrome: chromeApi,
              window: windowRef,
              document: documentRef,
              send,
              showToast,
              translate: (key, params) => translator(key, params),
              context: () => ({ root: context()?.root, settingsDialog: composition()?.settingsView?.getDialog?.() }),
              ui: Ui,
              onLayerChange: syncHost
            });
            activation.start();
            return activation;
          }
          function tabActionRuntime() {
            if (!tabActions) tabActions = TabActions.createTabActions({ send });
            return tabActions;
          }
          function compositionOptions(deps) {
            environmentState.getComposition = deps.getComposition;
            environmentState.getRecentlyClosed = deps.getRecentlyClosed;
            const workspace = sharedShell.workspaceBridge;
            const ports = {
              send,
              instanceCurrent: () => owner.state === "active" && windowRef.__TFA_OWNER__ === owner,
              getHost: () => context()?.host,
              getRoot: () => context()?.root,
              getEls: () => context()?.els || {},
              overlayVisible: workspace.overlayVisible,
              stylesReady: () => context()?.host?.getAttribute("data-styles-ready") === "true",
              recoverHost,
              ensureHost,
              showToast,
              toggleDockSite: (site) => tabActionRuntime().toggleDockSite(site),
              animateRemoval,
              getItems: items,
              getAnimatedItems: animatedItems,
              dockItemMarkup,
              dockSettingsFaviconMarkup,
              bindSettingsFaviconFallbacks,
              syncSettingsFaviconState,
              collectPageIconCandidates,
              devicePixelRatio: () => windowRef.devicePixelRatio || 1,
              createSpringState: () => ({ scale: { value: 1, velocity: 0 }, x: { value: 0, velocity: 0 }, y: { value: 0, velocity: 0 } }),
              writeTransform: (item, target, writeOptions) => runtime()?.writeTransform?.(item, target, writeOptions),
              prefersReducedMotion: () => Boolean(windowRef.matchMedia?.("(prefers-reduced-motion: reduce)").matches),
              settleForTabSwitch,
              cancelDrag: () => runtime()?.cancelDrag?.({ clearClickSuppression: true, animateRestore: false }),
              cancelSettlingDrag: () => runtime()?.cancelSettlingDrag?.(),
              pauseOverlaySession: workspace.pauseOverlaySession,
              resumeOverlaySession: workspace.resumeOverlaySession,
              resetCss: overlayRuntime.resetCss,
              preloadCss: overlayRuntime.preloadCss,
              renderTabs: workspace.renderTabs,
              updateCount: workspace.updateCount,
              closeAllMenus: workspace.closeAllMenus,
              syncHost,
              renderActivation: () => activationRuntime().render(),
              refreshLicense: () => activationRuntime().refresh(),
              closePurchasePreview: (restore) => activationRuntime().closePurchasePreview(restore),
              closeDeactivation: (restore) => activationRuntime().closeDeactivationDialog(restore),
              toggleRecentlyClosed: () => recent()?.toggle?.(),
              closeRecentlyClosed: () => recent()?.close?.(),
              translate: (key, params) => translator(key, params),
              loadLanguagePreference,
              saveLanguagePreference,
              onRecoveryCircuitOpen: () => authority?.pause?.(),
              aiHostPermissionAlreadyGranted: true
            };
            return {
              env: {
                chrome: chromeApi,
                document: documentRef,
                window: windowRef,
                screen: windowRef.screen,
                location: windowRef.location,
                MutationObserver: windowRef.MutationObserver,
                FileReader: windowRef.FileReader,
                Image: windowRef.Image,
                setTimeoutFn: windowRef.setTimeout.bind(windowRef),
                clearTimeoutFn: windowRef.clearTimeout.bind(windowRef),
                requestFrame: windowRef.requestAnimationFrame.bind(windowRef),
                cancelFrame: windowRef.cancelAnimationFrame.bind(windowRef),
                now: () => Date.now()
              },
              dockLogic: DockLogic,
              uiLogic: Ui,
              catalog: Catalog,
              activationRuntime: activationRuntime(),
              limits: LIMITS,
              ports
            };
          }
          function createLifecycle() {
            const lifecycle = LifecycleRuntime.createLifecycleRuntime();
            lifecycle.register(() => activation?.destroy?.());
            lifecycle.register(() => tabActions?.destroy?.());
            lifecycle.subscribe(chromeApi.storage.onChanged, handleAppearanceStorageChange);
            themeRuntime = ThemeRuntime.create({
              chrome: chromeApi,
              window: windowRef,
              document: documentRef,
              context: () => ({ host: context()?.host, root: context()?.root }),
              translate: (key, params) => translator(key, params)
            });
            lifecycle.register(() => themeRuntime?.destroy?.());
            appearanceReady = Promise.all([
              loadLanguagePreference(),
              themeRuntime.start()
            ]).then(() => !appearanceDestroyed);
            function destroy() {
              appearanceDestroyed = true;
              appearanceEpoch += 1;
              languageRevision += 1;
              return lifecycle.destroy();
            }
            return { start: () => appearanceReady, destroy, lifecycle };
          }
          function createAuthority() {
            authority = AuthorityRuntime.createDockAuthorityRuntime({
              document: documentRef,
              MutationObserver: windowRef.MutationObserver,
              ownerId: extensionId,
              generation: 2
            });
            return authority;
          }
          function recentlyClosedOptions(deps) {
            const lifecycle = deps.lifecycle.lifecycle;
            const comp = deps.getComposition();
            lifecycle.subscribe(chromeApi.storage.onChanged, (changes, area) => comp.handleStorageChange(changes, area));
            DocumentDockLifecycle.create({
              lifecycle,
              document: documentRef,
              window: windowRef,
              current: comp.current,
              cancelDrag: () => comp.runtime?.cancelDrag?.({ clearClickSuppression: true }),
              getHost: () => context()?.host,
              recoverHost,
              updateSuppression: comp.updateFullscreen,
              startDock: () => true
            }).start();
            return {
              instanceCurrent: () => owner.state === "active" && windowRef.__TFA_OWNER__ === owner,
              getViewContext: () => ({ root: context()?.root, els: context()?.els || {} }),
              getRecentlyClosed: () => tabActionRuntime().getRecentlyClosed(),
              consumeRecentlyClosedItem: (itemId, url) => tabActionRuntime().consumeRecentlyClosedItem(itemId, url),
              restoreSession: (id) => tabActionRuntime().restoreSession(id),
              openTab: (url, active) => tabActionRuntime().newTab(url, active),
              iconFor: (item) => comp.faviconRuntime.chromeUrl(item) || "",
              escapeHtml: Ui.escapeHtml,
              showToast,
              settleForTabSwitch,
              translate: (key, params) => translator(key, params),
              setPointerInteractivity: () => syncHost(),
              closeOthers: () => comp.dialogRuntime?.closeTopmost?.()
            };
          }
          async function handleIntent(intent, deps) {
            const comp = deps.getComposition();
            if (intent.type === "open-settings") {
              if (!await appearanceReady) return false;
              ensureHost();
              await overlayRuntime.preloadCss();
              const section = ["activation", "web-dock", "privacy-data", "ai"].includes(intent.section) ? intent.section : "activation";
              return comp.dialogRuntime.openSettings(section, intent.trigger || context()?.els?.webDockSettings || null) !== false;
            }
            if (intent.type === "toggle-site" || intent.type === "set-site-fixed") {
              const site = intent.site || intent.tab;
              if (!site || typeof site.url !== "string") return false;
              if (intent.type === "set-site-fixed" && typeof intent.fixed === "boolean") {
                const fixed = DockLogic.isDockSiteFixed(comp.config(), site.url);
                if (fixed === intent.fixed) return true;
              }
              return await comp.controller.toggleSite(site) === true;
            }
            return false;
          }
          initializer = InitializerModule.createWebDockFeatureInitializer({
            sharedShell,
            webDockController: WebDockController,
            compositionOptions,
            createLifecycle,
            createAuthority,
            createRecentlyClosed: (deps) => RecentRuntime.createWebDockRecentlyClosedRuntime(deps),
            recentlyClosedOptions,
            handleIntent
          });
          return Object.freeze({ initializer, sharedShell, overlayRuntime, inspect: initializer.inspect });
        }
        return Object.freeze({ REQUIRED_IDS, LIMITS, createWebDockFeatureEnvironment });
      });
    }
  });

  // src/content/content-feature-registration.js
  var require_content_feature_registration = __commonJS({
    "src/content/content-feature-registration.js"(exports, module) {
      (function attachContentFeatureRegistration(root, factory) {
        const api = factory(root);
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaContentFeatureRegistration = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createContentFeatureRegistrationModule(root) {
        "use strict";
        function registerFeature(name, options = {}) {
          const identity = options.identity || root.TfaBuildIdentity || {};
          const host = options.host || root.__TFA_FEATURE_HOST__;
          const initializers = options.initializers || root.TfaContentFeatureInitializers;
          const initializer = initializers?.[name];
          if (!host?.registerFeature || !initializer?.start || !initializer?.destroy) return false;
          return host.registerFeature({
            name,
            fingerprint: String(identity.fingerprint || `legacy:${identity.version || "unknown"}`),
            start: (intent) => initializer.start(intent),
            destroy: () => initializer.destroy()
          });
        }
        return Object.freeze({ registerFeature });
      });
    }
  });

  // src/content/web-dock-entry.js
  var require_web_dock_entry = __commonJS({
    "src/content/web-dock-entry.js"() {
      var import_version = __toESM(require_version());
      var import_dock_logic = __toESM(require_dock_logic());
      var import_dock_search_contract = __toESM(require_dock_search_contract());
      var import_web_dock_catalog = __toESM(require_web_dock_catalog());
      var import_web_dock_recently_closed_logic = __toESM(require_web_dock_recently_closed_logic());
      var import_browser_adapter = __toESM(require_browser_adapter());
      var import_i18n = __toESM(require_i18n());
      var import_content_ui_logic = __toESM(require_content_ui_logic());
      var import_app_context = __toESM(require_app_context());
      var import_lifecycle_runtime = __toESM(require_lifecycle_runtime());
      var import_overlay_template = __toESM(require_overlay_template());
      var import_overlay_runtime = __toESM(require_overlay_runtime());
      var import_theme_runtime = __toESM(require_theme_runtime());
      var import_activation_runtime = __toESM(require_activation_runtime());
      var import_tab_actions = __toESM(require_tab_actions());
      var import_dock_favicon_runtime = __toESM(require_dock_favicon_runtime());
      var import_dock_authority_runtime = __toESM(require_dock_authority_runtime());
      var import_dock_settings_runtime = __toESM(require_dock_settings_runtime());
      var import_dock_settings_preview = __toESM(require_dock_settings_preview());
      var import_dock_settings_view_markup = __toESM(require_dock_settings_view_markup());
      var import_dock_settings_ai_section = __toESM(require_dock_settings_ai_section());
      var import_dock_settings_backup_section = __toESM(require_dock_settings_backup_section());
      var import_dock_settings_view = __toESM(require_dock_settings_view());
      var import_dock_add_dialog_view = __toESM(require_dock_add_dialog_view());
      var import_dock_add_dialog = __toESM(require_dock_add_dialog());
      var import_dock_search_dialog = __toESM(require_dock_search_dialog());
      var import_dock_dialog_runtime = __toESM(require_dock_dialog_runtime());
      var import_web_dock_motion_runtime = __toESM(require_web_dock_motion_runtime());
      var import_web_dock_recently_closed_runtime = __toESM(require_web_dock_recently_closed_runtime());
      var import_web_dock_runtime = __toESM(require_web_dock_runtime());
      var import_v2_ai_settings_client = __toESM(require_v2_ai_settings_client());
      var import_web_dock_controller = __toESM(require_web_dock_controller());
      var import_document_dock_lifecycle_runtime = __toESM(require_document_dock_lifecycle_runtime());
      var import_content_feature_shared_shell = __toESM(require_content_feature_shared_shell());
      var import_web_dock_feature_initializer = __toESM(require_web_dock_feature_initializer());
      var import_web_dock_feature_environment = __toESM(require_web_dock_feature_environment());
      var import_content_feature_registration = __toESM(require_content_feature_registration());
      var webDockEnvironment = globalThis.TfaWebDockFeatureEnvironment.createWebDockFeatureEnvironment({
        window,
        document,
        chrome,
        owner: window.__TFA_OWNER__,
        identity: globalThis.TfaBuildIdentity,
        modules: {
          sharedShell: globalThis.TfaContentFeatureSharedShell,
          initializer: globalThis.TfaWebDockFeatureInitializer,
          overlayRuntime: globalThis.TfaOverlayRuntime,
          overlayTemplate: globalThis.TfaOverlayTemplate,
          themeRuntime: globalThis.TfaThemeRuntime,
          lifecycleRuntime: globalThis.TfaLifecycleRuntime,
          authorityRuntime: globalThis.TfaDockAuthorityRuntime,
          recentRuntime: globalThis.TfaWebDockRecentlyClosedRuntime,
          documentDockLifecycle: globalThis.TfaDocumentDockLifecycle,
          activationRuntime: globalThis.TfaActivationRuntime,
          tabActions: globalThis.TfaTabActions,
          webDockController: globalThis.TfaWebDockController,
          dockLogic: globalThis.TfaDockLogic,
          uiLogic: globalThis.TfaContentUiLogic,
          catalog: globalThis.TfaWebDockCatalog,
          i18n: globalThis.TfaI18n
        }
      });
      globalThis.TfaContentFeatureInitializers = globalThis.TfaContentFeatureInitializers || {};
      globalThis.TfaContentFeatureInitializers.web_dock = webDockEnvironment.initializer;
      globalThis.TfaContentFeatureRegistration.registerFeature("web_dock");
    }
  });
  require_web_dock_entry();
})();
