(() => {
  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/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/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/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/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/shared/newtab-site-space-model.js
  var require_newtab_site_space_model = __commonJS({
    "src/shared/newtab-site-space-model.js"(exports, module) {
      (function attachNewTabSiteSpaceModel(root, factory) {
        const api = factory(
          root.TfaDockLogic || (typeof __require === "function" ? require_dock_logic() : null),
          root.TfaWebDockCatalog || (typeof __require === "function" ? require_web_dock_catalog() : null)
        );
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaNewTabSiteSpaceModel = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createNewTabSiteSpaceModel(DockLogic, WebDockCatalog) {
        "use strict";
        const STORAGE_KEY = "tfa_newtab_site_space_v1";
        const SCHEMA_VERSION = 3;
        const DISPLAY_DEFAULTS = Object.freeze({ iconSize: 52, titleSize: 12 });
        const DISPLAY_LIMITS = Object.freeze({
          iconSize: Object.freeze({ min: 34, max: 64 }),
          titleSize: Object.freeze({ min: 10, max: 16 })
        });
        const TEXT_ICON_COLORS = DockLogic?.DOCK_TEXT_ICON_COLORS || Object.freeze([
          Object.freeze({ id: "violet", hex: "#7568dc" })
        ]);
        const COLOR_IDS = new Set(TEXT_ICON_COLORS.map((item) => item.id));
        const SAFE_ICON_KINDS = /* @__PURE__ */ new Set(["catalog", "favicon", "letter"]);
        const isChinese = (language) => String(language || "").toLowerCase().startsWith("zh");
        const safeText = (value, limit = 80) => String(value || "").trim().slice(0, limit);
        const safeId = (value) => safeText(value, 96).replace(/[^a-zA-Z0-9_-]/g, "");
        const safeTimestamp = (value, fallback) => Number.isFinite(Number(value)) && Number(value) > 0 ? Number(value) : fallback;
        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 clampDisplayValue(value, key) {
          const limits = DISPLAY_LIMITS[key];
          const fallback = DISPLAY_DEFAULTS[key];
          const number = Number(value);
          if (!limits || !Number.isFinite(number)) return fallback;
          return Math.round(Math.min(limits.max, Math.max(limits.min, number)));
        }
        function normalizeDisplay(value = {}) {
          return {
            iconSize: clampDisplayValue(value?.iconSize, "iconSize"),
            titleSize: clampDisplayValue(value?.titleSize, "titleSize")
          };
        }
        const LEGACY_DEFAULT_DEFINITION = Object.freeze([
          Object.freeze({ id: "work", titles: Object.freeze(["\u5DE5\u4F5C\u4E0E\u534F\u4F5C", "Work and collaboration"]), siteIds: Object.freeze(["gmail", "notion", "feishu", "tencentdocs", "googledrive"]) }),
          Object.freeze({ id: "ai-create", titles: Object.freeze(["AI \u4E0E\u521B\u4F5C", "AI and creation"]), siteIds: Object.freeze(["chatgpt", "claude", "gemini", "deepseek", "qwen", "figma", "canva"]) }),
          Object.freeze({ id: "knowledge", titles: Object.freeze(["\u77E5\u8BC6\u4E0E\u8D44\u8BAF", "Knowledge and news"]), siteIds: Object.freeze(["zhihu", "wikipedia", "bilibili", "youtube", "36kr", "github"]) }),
          Object.freeze({ id: "social", titles: Object.freeze(["\u793E\u4EA4\u4E0E\u793E\u533A", "Social and community"]), siteIds: Object.freeze(["wechat", "xiaohongshu", "weibo", "x", "reddit"]) }),
          Object.freeze({ id: "life", titles: Object.freeze(["\u751F\u6D3B\u4E0E\u5A31\u4E50", "Life and entertainment"]), siteIds: Object.freeze(["taobao", "jd", "meituan", "ctrip", "amap", "neteasemusic"]) })
        ]);
        const LEGACY_SITE_DEFINITION = Object.freeze({
          gmail: ["Gmail", "https://mail.google.com/"],
          notion: ["Notion", "https://www.notion.so/"],
          feishu: ["\u98DE\u4E66", "https://www.feishu.cn/"],
          tencentdocs: ["\u817E\u8BAF\u6587\u6863", "https://docs.qq.com/"],
          googledrive: ["Google Drive", "https://drive.google.com/"],
          chatgpt: ["ChatGPT", "https://chatgpt.com/"],
          claude: ["Claude", "https://claude.ai/"],
          gemini: ["Gemini", "https://gemini.google.com/"],
          deepseek: ["DeepSeek", "https://chat.deepseek.com/"],
          qwen: ["\u901A\u4E49\u5343\u95EE", "https://www.qianwen.com/"],
          figma: ["Figma", "https://www.figma.com/"],
          canva: ["Canva", "https://www.canva.com/"],
          zhihu: ["\u77E5\u4E4E", "https://www.zhihu.com/"],
          wikipedia: ["Wikipedia", "https://www.wikipedia.org/"],
          bilibili: ["\u54D4\u54E9\u54D4\u54E9", "https://www.bilibili.com/"],
          youtube: ["YouTube", "https://www.youtube.com/"],
          "36kr": ["36\u6C2A", "https://36kr.com/"],
          github: ["GitHub", "https://github.com/"],
          wechat: ["\u5FAE\u4FE1", "https://weixin.qq.com/"],
          xiaohongshu: ["\u5C0F\u7EA2\u4E66", "https://www.xiaohongshu.com/"],
          weibo: ["\u5FAE\u535A", "https://weibo.com/"],
          x: ["X", "https://x.com/"],
          reddit: ["Reddit", "https://www.reddit.com/"],
          taobao: ["\u6DD8\u5B9D", "https://www.taobao.com/"],
          jd: ["\u4EAC\u4E1C", "https://www.jd.com/"],
          meituan: ["\u7F8E\u56E2", "https://www.meituan.com/"],
          ctrip: ["\u643A\u7A0B", "https://www.ctrip.com/"],
          amap: ["\u9AD8\u5FB7\u5730\u56FE", "https://www.amap.com/"],
          neteasemusic: ["\u7F51\u6613\u4E91\u97F3\u4E50", "https://music.163.com/"]
        });
        const DEFAULT_DEFINITION = Object.freeze([
          Object.freeze({ id: "work", zh: "\u5DE5\u4F5C\u4E0E\u534F\u4F5C", en: "Work and collaboration", siteIds: Object.freeze([
            "gmail",
            "outlook",
            "notion",
            "googledrive",
            "onedrive",
            "dropbox",
            "box",
            "slack",
            "zoom",
            "trello",
            "asana",
            "airtable",
            "evernote",
            "feishu",
            "dingtalk",
            "tencentdocs",
            "wps",
            "baidupan",
            "google",
            "bing"
          ]) }),
          Object.freeze({ id: "ai-create", zh: "AI \u4E0E\u521B\u4F5C", en: "AI and creation", siteIds: Object.freeze([
            "chatgpt",
            "claude",
            "gemini",
            "copilot",
            "perplexity",
            "grok",
            "deepseek",
            "qwen",
            "kimi",
            "doubao",
            "wenxin",
            "yuanbao",
            "zhipu",
            "mistral",
            "huggingface",
            "poe",
            "cohere",
            "characterai",
            "midjourney",
            "suno"
          ]) }),
          Object.freeze({ id: "knowledge", zh: "\u77E5\u8BC6\u4E0E\u8D44\u8BAF", en: "Knowledge and news", siteIds: Object.freeze([
            "zhihu",
            "wikipedia",
            "medium",
            "quora",
            "substack",
            "gitbook",
            "archive",
            "coursera",
            "edx",
            "36kr",
            "huxiu",
            "thepaper",
            "caixin",
            "nytimes",
            "bbc",
            "reuters",
            "bloomberg",
            "guardian",
            "wsj",
            "ft",
            "npr",
            "apnews",
            "economist"
          ]) }),
          Object.freeze({ id: "design-dev", zh: "\u8BBE\u8BA1\u4E0E\u5F00\u53D1", en: "Design and development", siteIds: Object.freeze([
            "figma",
            "canva",
            "miro",
            "excalidraw",
            "adobe",
            "photoshop",
            "dribbble",
            "behance",
            "unsplash",
            "pexels",
            "freepik",
            "github",
            "gitlab",
            "stackoverflow",
            "mdn",
            "vercel",
            "docker",
            "leetcode",
            "gitee",
            "juejin",
            "csdn",
            "cnblogs",
            "segmentfault",
            "vscode"
          ]) }),
          Object.freeze({ id: "social-media", zh: "\u793E\u4EA4\u4E0E\u5F71\u97F3", en: "Social and media", siteIds: Object.freeze([
            "wechat",
            "qq",
            "weibo",
            "x",
            "reddit",
            "linkedin",
            "telegram",
            "discord",
            "xiaohongshu",
            "bilibili",
            "youtube",
            "douyin",
            "tiktok",
            "spotify",
            "neteasemusic",
            "qqmusic",
            "ximalaya",
            "iqiyi",
            "tencentvideo",
            "douban"
          ]) }),
          Object.freeze({ id: "life", zh: "\u751F\u6D3B\u4E0E\u6D88\u8D39", en: "Life and shopping", siteIds: Object.freeze([
            "taobao",
            "tmall",
            "jd",
            "pinduoduo",
            "amazon",
            "meituan",
            "dianping",
            "ctrip",
            "airbnb",
            "amap",
            "baidumaps",
            "eleme",
            "xianyu",
            "suning",
            "dangdang",
            "vipshop",
            "qunar"
          ]) })
        ]);
        const CATALOG_BY_ID = new Map((WebDockCatalog?.sites || []).map((site) => [site.id, site]));
        function isUntouchedLegacySpace(value) {
          if (Number(value?.schemaVersion) >= SCHEMA_VERSION) return false;
          if (!Array.isArray(value?.categories) || !Array.isArray(value?.sites)) return false;
          if (Number(value.revision) !== 1) return false;
          if (value.categories.length !== LEGACY_DEFAULT_DEFINITION.length) return false;
          const categories = value.categories.slice().sort((a, b) => Number(a?.order) - Number(b?.order));
          if (!LEGACY_DEFAULT_DEFINITION.every((group, order) => {
            const item = categories[order];
            return item && safeId(item.id) === group.id && Number(item.order) === order && group.titles.includes(safeText(item.title, 40));
          })) return false;
          if (value.sites.length !== 29) return false;
          const sites = value.sites.slice().sort((a, b) => {
            const categoryOrder = (id) => LEGACY_DEFAULT_DEFINITION.findIndex((group) => group.id === safeId(id));
            return categoryOrder(a?.categoryId) - categoryOrder(b?.categoryId) || Number(a?.order) - Number(b?.order);
          });
          let offset = 0;
          return LEGACY_DEFAULT_DEFINITION.every((group) => group.siteIds.every((id, order) => {
            const item = sites[offset++];
            const definition = LEGACY_SITE_DEFINITION[id];
            return item && definition && safeId(item.id) === id && safeId(item.categoryId) === group.id && Number(item.order) === order && safeText(item.title, 80) === definition[0] && safeUrl(item.url) === definition[1] && item.iconKind === "catalog" && safeText(item.iconValue, 160).replace(/[^a-zA-Z0-9_./-]/g, "") === `web-dock-icons/${id}.png` && item.iconColor === (TEXT_ICON_COLORS[0]?.id || "violet");
          }));
        }
        function createDefaultSpace({ now = Date.now(), language = "zh" } = {}) {
          const timestamp = Number(now) || Date.now();
          const categories = DEFAULT_DEFINITION.map((group, order) => ({
            id: group.id,
            title: isChinese(language) ? group.zh : group.en,
            order,
            createdAt: timestamp,
            updatedAt: timestamp
          }));
          const sites = DEFAULT_DEFINITION.flatMap((group) => group.siteIds.map((siteId, order) => {
            const site = CATALOG_BY_ID.get(siteId);
            if (!site) return null;
            return {
              id: site.id,
              categoryId: group.id,
              title: site.title,
              url: site.url,
              iconKind: "catalog",
              iconValue: site.iconFile,
              iconColor: TEXT_ICON_COLORS[0]?.id || "violet",
              order,
              createdAt: timestamp,
              updatedAt: timestamp
            };
          }).filter(Boolean));
          return {
            schemaVersion: SCHEMA_VERSION,
            revision: 1,
            categories,
            sites,
            display: normalizeDisplay(),
            updatedAt: timestamp
          };
        }
        function normalizeSpace(value, options = {}) {
          const now = Number(options.now) || Date.now();
          if (!value || typeof value !== "object" || !Array.isArray(value.categories) || !Array.isArray(value.sites)) {
            return createDefaultSpace({ now, language: options.language });
          }
          if (isUntouchedLegacySpace(value)) return createDefaultSpace({ now, language: options.language });
          const seenCategories = /* @__PURE__ */ new Set();
          const categories = value.categories.map((item, index) => {
            const id = safeId(item?.id);
            const title = safeText(item?.title, 40);
            if (!id || !title || seenCategories.has(id)) return null;
            seenCategories.add(id);
            return {
              id,
              title,
              order: Number.isFinite(Number(item.order)) ? Number(item.order) : index,
              createdAt: safeTimestamp(item.createdAt, now),
              updatedAt: safeTimestamp(item.updatedAt, now)
            };
          }).filter(Boolean).sort((a, b) => a.order - b.order).map((item, order) => ({ ...item, order }));
          const categoryIds = new Set(categories.map((item) => item.id));
          const seenSites = /* @__PURE__ */ new Set();
          const sites = value.sites.map((item, index) => {
            const id = safeId(item?.id);
            const categoryId = safeId(item?.categoryId);
            const title = safeText(item?.title, 80);
            const url = safeUrl(item?.url);
            if (!id || !categoryIds.has(categoryId) || !title || !url || seenSites.has(id)) return null;
            seenSites.add(id);
            const iconKind = SAFE_ICON_KINDS.has(item?.iconKind) ? item.iconKind : "favicon";
            const iconValue = iconKind === "catalog" ? safeText(item?.iconValue, 160).replace(/[^a-zA-Z0-9_./-]/g, "") : safeText(item?.iconValue, 240);
            return {
              id,
              categoryId,
              title,
              url,
              iconKind,
              iconValue,
              iconColor: COLOR_IDS.has(item?.iconColor) ? item.iconColor : TEXT_ICON_COLORS[0]?.id || "violet",
              order: Number.isFinite(Number(item.order)) ? Number(item.order) : index,
              createdAt: safeTimestamp(item.createdAt, now),
              updatedAt: safeTimestamp(item.updatedAt, now)
            };
          }).filter(Boolean);
          const orderedSites = categories.flatMap((category) => sites.filter((site) => site.categoryId === category.id).sort((a, b) => a.order - b.order).map((site, order) => ({ ...site, order })));
          return {
            schemaVersion: SCHEMA_VERSION,
            revision: Math.max(1, Number(value.revision) || 1),
            categories,
            sites: orderedSites,
            display: normalizeDisplay(value.display),
            updatedAt: safeTimestamp(value.updatedAt, now)
          };
        }
        return Object.freeze({
          STORAGE_KEY,
          SCHEMA_VERSION,
          DISPLAY_DEFAULTS,
          DISPLAY_LIMITS,
          TEXT_ICON_COLORS,
          DEFAULT_DEFINITION,
          safeText,
          safeId,
          safeUrl,
          normalizeDisplay,
          createDefaultSpace,
          normalizeSpace
        });
      });
    }
  });

  // 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/newtab/runtime.js
  var require_runtime = __commonJS({
    "src/newtab/runtime.js"(exports, module) {
      (function attachNewTabRuntime(root, factory) {
        const commonJs = typeof module === "object" && module.exports;
        const searchContract = commonJs ? require_dock_search_contract() : root.TfaDockSearchContract;
        const searchDialog = commonJs ? require_dock_search_dialog() : root.TfaDockSearchDialog;
        const api = factory(searchContract, searchDialog);
        if (typeof module === "object" && module.exports) module.exports = api;
        root.TfaNewTabRuntime = api;
      })(typeof globalThis === "object" ? globalThis : exports, function createNewTabRuntimeModule(defaultSearchContract, defaultSearchDialog) {
        "use strict";
        const TOGGLE_MESSAGE = "TFA_NEWTAB_TOGGLE_OVERLAY";
        const WORKSPACE_HOST_SELECTOR = '[data-tfa-host="true"]';
        const THEME_KEY = "tfa_theme";
        const LANGUAGE_KEY = "tfa_language";
        const DOCK_KEY = "tfa_web_dock";
        const SEARCH_DEBOUNCE_MS = 140;
        const COPY = Object.freeze({
          zh: Object.freeze({
            subtitle: "\u5E38\u7528\u7F51\u7AD9\u4E0E\u4EFB\u52A1",
            search: "\u641C\u7D22\u7F51\u7AD9\u3001\u9875\u7B7E\u3001\u4E66\u7B7E\u548C\u5386\u53F2",
            open: "\u6253\u5F00\u5DE5\u4F5C\u53F0",
            sites: "\u5E38\u7528\u7F51\u7AD9",
            tasks: "\u4EFB\u52A1\u603B\u89C8",
            local: "\u5DF2\u4FDD\u5B58\u5728\u672C\u673A",
            addSite: "\u65B0\u589E\u7F51\u7AD9",
            addCategory: "\u65B0\u589E\u5206\u7C7B",
            editHint: "\u70B9\u51FB\u4FEE\u6539\u5206\u7C7B\u540D\u79F0",
            editCategory: "\u7F16\u8F91\u5206\u7C7B",
            emptyDrop: "\u628A\u7F51\u7AD9\u62D6\u5230\u8FD9\u4E2A\u5206\u7C7B",
            editSite: "\u7F16\u8F91\u7F51\u7AD9",
            deleteSite: "\u5220\u9664\u7F51\u7AD9",
            pin: "\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            unpin: "\u53D6\u6D88\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            pinned: "\u5DF2\u56FA\u5B9A\u5230\u7F51\u9875\u62D3\u5C55\u575E",
            unpinned: "\u5DF2\u4ECE\u7F51\u9875\u62D3\u5C55\u575E\u53D6\u6D88\u56FA\u5B9A",
            saved: "\u5DF2\u4FDD\u5B58",
            deleted: "\u5DF2\u5220\u9664",
            failed: "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
            conflict: "\u53E6\u4E00\u65B0\u6807\u7B7E\u9875\u521A\u521A\u66F4\u65B0\u4E86\u5185\u5BB9\uFF0C\u5DF2\u540C\u6B65\u6700\u65B0\u6570\u636E",
            invalidUrl: "\u8BF7\u8F93\u5165\u6709\u6548\u7F51\u5740\uFF0C\u4F8B\u5982 baidu.com",
            duplicate: "\u8FD9\u4E2A\u7F51\u7AD9\u5DF2\u7ECF\u5728\u5E38\u7528\u7F51\u7AD9\u4E2D",
            deleteCategory: "\u5220\u9664\u5206\u7C7B\u540E\uFF0C\u5176\u4E2D\u7684\u7F51\u7AD9\u4E5F\u4F1A\u5220\u9664\u3002\u786E\u5B9A\u7EE7\u7EED\u5417\uFF1F",
            custom: "\u81EA\u5B9A\u4E49\u65B0\u589E",
            popular: "\u9009\u62E9\u5E38\u7528\u7F51\u7AD9",
            bookmarks: "\u4ECE\u4E66\u7B7E\u5BFC\u5165",
            bookmarkLoading: "\u6B63\u5728\u8BFB\u53D6\u4E66\u7B7E\u2026",
            bookmarkEmpty: "\u6CA1\u6709\u53EF\u5BFC\u5165\u7684\u4E66\u7B7E",
            bookmarkFailed: "\u65E0\u6CD5\u8BFB\u53D6\u4E66\u7B7E\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5",
            bookmarkPermission: "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E",
            selectOne: "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u7F51\u7AD9",
            searchEmpty: "\u8F93\u5165\u5173\u952E\u8BCD\u641C\u7D22\u5E38\u7528\u3001\u56FA\u5B9A\u3001\u9875\u7B7E\u3001\u4E66\u7B7E\u4E0E\u6D4F\u89C8\u5386\u53F2",
            noResults: "\u6CA1\u6709\u5339\u914D\u7ED3\u679C",
            allowBookmarks: "\u5141\u8BB8\u8BFB\u53D6\u4E66\u7B7E",
            allowHistory: "\u5141\u8BB8\u8BFB\u53D6\u6D4F\u89C8\u5386\u53F2",
            permissionRequired: "\u5C1A\u672A\u6388\u6743",
            permissionDenied: "\u5DF2\u62D2\u7EDD\u6D4F\u89C8\u5668\u6743\u9650\uFF0C\u53EF\u7A0D\u540E\u91CD\u8BD5",
            searchWeb: "\u5728\u7F51\u9875\u4E2D\u641C\u7D22",
            sourceCommon: "\u5E38\u7528",
            sourceFixed: "\u56FA\u5B9A",
            sourceTab: "\u9875\u7B7E",
            sourceBookmark: "\u4E66\u7B7E",
            sourceBrowser: "\u5386\u53F2",
            taskEmpty: "\u8FD8\u6CA1\u6709\u4EFB\u52A1",
            taskCreate: "\u5728\u5DE5\u4F5C\u53F0\u4E2D\u521B\u5EFA",
            taskOpen: "\u6253\u5F00\u5168\u90E8",
            taskClose: "\u5173\u95ED\u5168\u90E8",
            taskActive: "\u8FDB\u884C\u4E2D",
            taskPaused: "\u5DF2\u6682\u505C",
            taskDone: "\u5DF2\u5B8C\u6210",
            taskPending: "\u5F85\u5904\u7406",
            taskOpened: "\u4EFB\u52A1\u9875\u9762\u5DF2\u6253\u5F00",
            taskClosed: "\u5DF2\u5173\u95ED\u5F53\u524D\u6253\u5F00\u7684\u4EFB\u52A1\u9875\u7B7E",
            taskNoOpen: "\u8FD9\u4E2A\u4EFB\u52A1\u6CA1\u6709\u5DF2\u6253\u5F00\u7684\u9875\u7B7E",
            taskUpdated: "\u4EFB\u52A1\u72B6\u6001\u5DF2\u66F4\u65B0",
            taskRename: "\u6539\u540D",
            taskRenamePrompt: "\u8F93\u5165\u65B0\u7684\u4EFB\u52A1\u540D\u79F0",
            taskRenamed: "\u4EFB\u52A1\u540D\u79F0\u5DF2\u66F4\u65B0",
            taskDomainUpdated: "\u4EFB\u52A1\u9886\u57DF\u5DF2\u66F4\u65B0",
            taskMore: "\u66F4\u591A",
            taskDissolve: "\u89E3\u6563\u4EFB\u52A1",
            taskDissolveConfirm: "\u89E3\u6563\u4EFB\u52A1\u4F1A\u79FB\u9664\u5176\u4E2D\u7684\u4EFB\u52A1\u5173\u7CFB\uFF0C\u786E\u5B9A\u7EE7\u7EED\u5417\uFF1F",
            taskDissolved: "\u4EFB\u52A1\u5DF2\u89E3\u6563",
            taskPageOpen: "\u6253\u5F00\u9875\u7B7E",
            taskPageRemove: "\u79FB\u51FA\u4EFB\u52A1",
            taskPageRemoved: "\u5DF2\u4ECE\u4EFB\u52A1\u79FB\u51FA",
            taskPageMoved: "\u4EFB\u52A1\u9875\u7B7E\u5DF2\u79FB\u52A8",
            taskPageReordered: "\u4EFB\u52A1\u9875\u7B7E\u987A\u5E8F\u5DF2\u66F4\u65B0",
            siteAssigned: "\u7F51\u7AD9\u5DF2\u52A0\u5165\u4EFB\u52A1",
            taskDropSite: "\u62D6\u5230\u8FD9\u91CC\u52A0\u5165\u4EFB\u52A1",
            taskPageOpened: "\u5DF2\u6253\u5F00",
            taskPageClosed: "\u672A\u6253\u5F00",
            taskSourceManual: "\u624B\u52A8\u5206\u7C7B",
            taskSourceSmart: "\u667A\u80FD\u5206\u7C7B",
            taskPrimary: "\u4E3B\u8981\u4EFB\u52A1",
            taskChooseReplacement: "\u8BF7\u9009\u62E9\u65B0\u7684\u4E3B\u8981\u4EFB\u52A1",
            taskReplacementCancelled: "\u5DF2\u53D6\u6D88\u79FB\u51FA",
            domainWork: "\u5DE5\u4F5C",
            domainStudy: "\u5B66\u4E60",
            domainLife: "\u751F\u6D3B",
            domainInterest: "\u5174\u8DA3",
            domainAdmin: "\u4E8B\u52A1",
            domainOther: "\u5176\u4ED6",
            iconSize: "\u56FE\u6807\u5C3A\u5BF8",
            titleSize: "\u6807\u9898\u5B57\u53F7",
            pageActions: "\u9875\u9762\u64CD\u4F5C",
            theme: "\u5207\u6362\u4E3B\u9898",
            settings: "\u8BBE\u7F6E",
            refreshTasks: "\u5237\u65B0\u4EFB\u52A1",
            taskStatusChange: "\u5207\u6362\u4EFB\u52A1\u72B6\u6001",
            emptySites: "\u8FD8\u6CA1\u6709\u5E38\u7528\u7F51\u7AD9",
            emptySitesDescription: "\u521B\u5EFA\u5206\u7C7B\uFF0C\u518D\u52A0\u5165\u6BCF\u5929\u4F1A\u6253\u5F00\u7684\u7F51\u7AD9\u3002",
            retry: "\u91CD\u8BD5",
            loadFailed: "TabFlow \u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D",
            siteDescription: "\u9009\u62E9\u4E00\u79CD\u65B9\u5F0F\u52A0\u5165\u5E38\u7528\u7F51\u7AD9\u3002",
            addMethods: "\u65B0\u589E\u7F51\u7AD9\u65B9\u5F0F",
            popularSearch: "\u641C\u7D22\u5E38\u7528\u7F51\u7AD9",
            cancel: "\u53D6\u6D88",
            saveSite: "\u4FDD\u5B58\u7F51\u7AD9",
            categoryName: "\u5206\u7C7B\u540D\u79F0",
            saveCategory: "\u4FDD\u5B58\u5206\u7C7B",
            close: "\u5173\u95ED",
            searchTitle: "\u641C\u7D22",
            searchDescription: "\u5E38\u7528\u3001\u56FA\u5B9A\u3001\u9875\u7B7E\u3001\u4E66\u7B7E\u4E0E\u6D4F\u89C8\u5386\u53F2",
            searchKeywords: "\u8F93\u5165\u5173\u952E\u8BCD",
            editorCategory: "\u6240\u5C5E\u5206\u7C7B",
            editorTitle: "\u7F51\u7AD9\u6807\u9898",
            editorUrl: "\u7F51\u9875\u7F51\u5740",
            editorIcon: "\u7F51\u7AD9\u56FE\u6807",
            editorIconSource: "\u56FE\u6807\u6765\u6E90",
            editorAuto: "\u81EA\u52A8\u83B7\u53D6",
            editorText: "\u6587\u5B57\u56FE\u6807",
            editorUpload: "\u672C\u5730\u4E0A\u4F20",
            editorTextLabel: "\u56FE\u6807\u6587\u5B57",
            editorTextPlaceholder: "\u6700\u591A 10 \u4E2A\u4E2D\u82F1\u6587\u5B57\u6BCD",
            editorColor: "\u6587\u5B57\u56FE\u6807\u989C\u8272",
            editorUploadChoose: "\u9009\u62E9\u672C\u5730\u56FE\u7247",
            editorUploadHint: "PNG / JPEG / WebP / SVG\uFF0C\u4E0D\u8D85\u8FC7 512KB"
          }),
          en: Object.freeze({
            subtitle: "Sites and tasks",
            search: "Search sites, tabs, bookmarks, and history",
            open: "Open workspace",
            sites: "Favorite sites",
            tasks: "Task overview",
            local: "Saved on this device",
            addSite: "Add site",
            addCategory: "Add category",
            editHint: "Click to rename category",
            editCategory: "Edit category",
            emptyDrop: "Drop sites into this category",
            editSite: "Edit site",
            deleteSite: "Delete site",
            pin: "Pin to Web Dock",
            unpin: "Remove from Web Dock",
            pinned: "Pinned to Web Dock",
            unpinned: "Removed from Web Dock",
            saved: "Saved",
            deleted: "Deleted",
            failed: "Something went wrong. Please try again.",
            conflict: "Another new tab changed this space. The latest version is now shown.",
            invalidUrl: "Enter a valid address such as example.com",
            duplicate: "This site is already in your favorites",
            deleteCategory: "Deleting this category also removes its sites. Continue?",
            custom: "Add custom",
            popular: "Popular sites",
            bookmarks: "Import bookmarks",
            bookmarkLoading: "Loading bookmarks\u2026",
            bookmarkEmpty: "No bookmarks to import",
            bookmarkFailed: "Bookmarks could not be read. Try again later.",
            bookmarkPermission: "Allow bookmarks",
            selectOne: "Select at least one site",
            searchEmpty: "Type to search favorites, pinned sites, tabs, bookmarks, and history",
            noResults: "No matching results",
            allowBookmarks: "Allow bookmarks",
            allowHistory: "Allow browser history",
            permissionRequired: "Not authorized yet",
            permissionDenied: "Browser permission was denied. Try again later.",
            searchWeb: "Search the web",
            sourceCommon: "Favorite",
            sourceFixed: "Pinned",
            sourceTab: "Tab",
            sourceBookmark: "Bookmark",
            sourceBrowser: "History",
            taskEmpty: "No tasks yet",
            taskCreate: "Create in workspace",
            taskOpen: "Open all",
            taskClose: "Close all",
            taskActive: "Active",
            taskPaused: "Paused",
            taskDone: "Done",
            taskPending: "Pending",
            taskOpened: "Task pages opened",
            taskClosed: "Open task tabs closed",
            taskNoOpen: "This task has no open tabs",
            taskUpdated: "Task status updated",
            taskRename: "Rename",
            taskRenamePrompt: "Enter a new task name",
            taskRenamed: "Task name updated",
            taskDomainUpdated: "Task domain updated",
            taskMore: "More",
            taskDissolve: "Dissolve task",
            taskDissolveConfirm: "Dissolving this task removes its task relations. Continue?",
            taskDissolved: "Task dissolved",
            taskPageOpen: "Open tab",
            taskPageRemove: "Remove from task",
            taskPageRemoved: "Removed from task",
            taskPageMoved: "Task tab moved",
            taskPageReordered: "Task tab order updated",
            siteAssigned: "Site added to task",
            taskDropSite: "Drop here to add to task",
            taskPageOpened: "Open",
            taskPageClosed: "Closed",
            taskSourceManual: "Manual",
            taskSourceSmart: "Smart",
            taskPrimary: "Primary task",
            taskChooseReplacement: "Choose the new primary task",
            taskReplacementCancelled: "Removal cancelled",
            domainWork: "Work",
            domainStudy: "Study",
            domainLife: "Life",
            domainInterest: "Interest",
            domainAdmin: "Admin",
            domainOther: "Other",
            iconSize: "Icon size",
            titleSize: "Title size",
            pageActions: "Page actions",
            theme: "Toggle theme",
            settings: "Settings",
            refreshTasks: "Refresh tasks",
            taskStatusChange: "Change task status",
            emptySites: "No favorite sites yet",
            emptySitesDescription: "Create a category, then add sites you use every day.",
            retry: "Retry",
            loadFailed: "TabFlow could not load",
            siteDescription: "Choose how to add a favorite site.",
            addMethods: "Ways to add a site",
            popularSearch: "Search popular sites",
            cancel: "Cancel",
            saveSite: "Save site",
            categoryName: "Category name",
            saveCategory: "Save category",
            close: "Close",
            searchTitle: "Search",
            searchDescription: "Favorites, pinned sites, tabs, bookmarks, and history",
            searchKeywords: "Enter keywords",
            editorCategory: "Category",
            editorTitle: "Site title",
            editorUrl: "Web address",
            editorIcon: "Site icon",
            editorIconSource: "Icon source",
            editorAuto: "Fetch automatically",
            editorText: "Text icon",
            editorUpload: "Local upload",
            editorTextLabel: "Icon text",
            editorTextPlaceholder: "Up to 10 letters",
            editorColor: "Text icon color",
            editorUploadChoose: "Choose a local image",
            editorUploadHint: "PNG / JPEG / WebP / SVG, up to 512 KB"
          })
        });
        const ICONS = Object.freeze({
          edit: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m4 16-.8 4 4-.8L18.5 7.9l-3.4-3.4L4 16Z"/><path d="m13.8 5.8 3.4 3.4"/></svg>',
          pin: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 3 8 0-.9 6 3 3v2H6v-2l3-3L8 3Z"/><path d="M12 14v7"/></svg>',
          delete: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3m-9 0 1 13h10l1-13M10 11v5m4-5v5"/></svg>',
          add: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>',
          open: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14 4h6v6M20 4l-9 9"/><path d="M19 13v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h6"/></svg>'
        });
        function isTargetMessage(message, currentTabId) {
          if (!message || message.type !== TOGGLE_MESSAGE) return false;
          const targetTabId = Number(message.tabId);
          return !Number.isInteger(targetTabId) || !Number.isInteger(currentTabId) || targetTabId === currentTabId;
        }
        function projectTaskData(response = {}) {
          if (!Array.isArray(response.pages) || !Array.isArray(response.relations)) {
            return Array.isArray(response.tasks) ? response.tasks : [];
          }
          const pages = new Map(response.pages.map((page) => [page.id, page]));
          const byTask = /* @__PURE__ */ new Map();
          response.relations.forEach((relation) => {
            const records = byTask.get(relation.taskId) || [];
            records.push(relation);
            byTask.set(relation.taskId, records);
          });
          return [...response.tasks || []].sort((a, b) => Number(a.sortOrder) - Number(b.sortOrder) || String(a.id).localeCompare(String(b.id))).map((task) => ({
            ...task,
            pages: [...byTask.get(task.id) || []].sort((a, b) => Number(a.sortOrder) - Number(b.sortOrder) || String(a.id).localeCompare(String(b.id))).map((relation) => {
              const page = pages.get(relation.pageId);
              return page ? {
                id: page.id,
                title: page.title,
                url: page.restoreUrl || page.normalizedUrl,
                favicon: page.faviconUrl || "",
                relation
              } : null;
            }).filter(Boolean)
          }));
        }
        function normalizeThemePreference(value) {
          const normalized = String(value || "").trim().toLowerCase();
          if (["system", "light", "dark"].includes(normalized)) {
            return { value: normalized, migrated: false };
          }
          if (["snow", "paper", "pro"].includes(normalized)) {
            return { value: "light", migrated: true };
          }
          if (["noir", "ocean", "space", "crypto"].includes(normalized)) {
            return { value: "dark", migrated: true };
          }
          return { value: "system", migrated: false };
        }
        function reorderIds(ids, sourceId, targetId = null, after = false) {
          const ordered = [...ids].map(String);
          const source = String(sourceId || "");
          const target = targetId == null ? null : String(targetId);
          const sourceIndex = ordered.indexOf(source);
          if (sourceIndex < 0 || target === source) return ordered;
          ordered.splice(sourceIndex, 1);
          if (target == null) {
            ordered.push(source);
            return ordered;
          }
          const targetIndex = ordered.indexOf(target);
          if (targetIndex < 0) return [...ids].map(String);
          ordered.splice(targetIndex + (after ? 1 : 0), 0, source);
          return ordered;
        }
        function insertIdRelative(ids, sourceId, targetId = null, after = false) {
          const source = String(sourceId || "");
          const ordered = [...ids].map(String).filter((id) => id !== source);
          if (!source) return ordered;
          if (targetId == null) return [...ordered, source];
          const target = String(targetId);
          const targetIndex = ordered.indexOf(target);
          if (targetIndex < 0) return [...ordered, source];
          ordered.splice(targetIndex + (after ? 1 : 0), 0, source);
          return ordered;
        }
        function replacementTaskCandidates(tasks, taskId, pageId) {
          return (Array.isArray(tasks) ? tasks : []).filter((candidate) => String(candidate?.id || "") !== String(taskId || "") && candidate?.pages?.some?.((page) => String(page?.id || "") === String(pageId || "")));
        }
        async function resolveReplacementTaskId(candidates, choose) {
          const choices = Array.isArray(candidates) ? candidates.filter((candidate) => candidate?.id) : [];
          if (!choices.length) return "";
          if (choices.length === 1) return String(choices[0].id);
          if (typeof choose !== "function") return null;
          const selected = await choose(choices);
          const id = typeof selected === "object" ? String(selected?.id || "") : String(selected || "");
          return choices.some((candidate) => String(candidate.id) === id) ? id : null;
        }
        function decodeDragPayload(value) {
          const raw = String(value || "");
          let match = raw.match(/^task-page:([^:]+):(.+)$/);
          if (match) return { type: "task-page", sourceTaskId: match[1], pageId: match[2] };
          match = raw.match(/^(site|category):(.+)$/);
          return match ? { type: match[1], id: match[2] } : null;
        }
        function createNewTabRuntime(options = {}) {
          const windowRef = options.window || globalThis.window;
          const documentRef = options.document || globalThis.document;
          const chromeApi = options.chrome || globalThis.chrome;
          const bootstrap = options.bootstrap;
          const model = options.model || globalThis.TfaNewTabSiteSpaceModel;
          const catalog = options.catalog || globalThis.TfaWebDockCatalog;
          const siteEditorModule = options.siteEditor || globalThis.TfaSiteEditorView;
          const i18n = options.i18n || globalThis.TfaI18n;
          const searchContract = options.searchContract || defaultSearchContract || globalThis.TfaDockSearchContract;
          const searchDialogModule = options.searchDialog || defaultSearchDialog || globalThis.TfaDockSearchDialog;
          if (!windowRef || !documentRef || !chromeApi?.runtime?.onMessage || !model?.normalizeSpace || !siteEditorModule?.createSiteEditorView || typeof bootstrap?.requestFeature !== "function" || !searchContract?.composeSuggestions || !searchDialogModule?.createDockSearchDialog) {
            throw new Error("new tab runtime dependencies unavailable");
          }
          let destroyed = false;
          let bound = false;
          let currentTabId = null;
          let observer = null;
          let space = null;
          let tasks = [];
          let openTabs = [];
          let dockUrls = /* @__PURE__ */ new Set();
          let editingSiteId = "";
          let editingCategoryId = "";
          let selectedColor = model.TEXT_ICON_COLORS[0]?.id || "violet";
          let selectedIconSource = "auto";
          let dragPayload = null;
          let lastTaskDragDiagnostic = null;
          let toastTimer = 0;
          let lastSearchPermissionBroadcastToken = "";
          let lastSearchPermissionBroadcastRevision = 0;
          let suppressQuickSearchFocus = false;
          let searchDialogRuntime = options.searchDialogRuntime || null;
          let searchDialogHost = null;
          let activeAddTab = "custom";
          let selectedPickUrls = /* @__PURE__ */ new Set();
          let bookmarks = [];
          let bookmarksLoaded = false;
          let bookmarkPermissionPending = false;
          let siteDialogCategoryId = "";
          let dragPlaceholder = null;
          let taskLoadSequence = 0;
          let taskRefreshWork = null;
          let taskRefreshPending = false;
          let taskRenderPending = false;
          let deferredTaskSnapshot = null;
          let dragRenderGuard = false;
          let dragRefreshFlushTimer = 0;
          let pendingTaskRefreshEpoch = 0;
          let lastTaskChangeEpoch = 0;
          let lastTaskChangeToken = "";
          let displaySaveTimer = 0;
          let pendingDisplay = null;
          let suppressSiteClickUntil = 0;
          let themePreference = "system";
          let themePreferenceRevision = 0;
          let themeWriteQueue = Promise.resolve();
          let themeLastQueuedRevision = -1;
          const localFeatureLoads = /* @__PURE__ */ new Map();
          const layoutAnimations = /* @__PURE__ */ new WeakMap();
          const colorSchemeQuery = windowRef.matchMedia?.("(prefers-color-scheme: dark)") || null;
          const browserLanguage = chromeApi.i18n?.getUILanguage?.() || windowRef.navigator?.language || "en";
          let language = i18n?.resolveLanguage ? i18n.resolveLanguage({ browserLanguage, navigatorLanguage: windowRef.navigator?.language }) : String(browserLanguage).toLowerCase().startsWith("zh") ? "zh" : "en";
          let copy = COPY[language];
          const el = (id) => documentRef.getElementById?.(id);
          const board = el("categoryBoard");
          const status = el("newtabStatus");
          const statusText = el("newtabStatusText");
          const retryButton = el("newtabRetry");
          const siteEditorView = siteEditorModule.createSiteEditorView({ textIconColors: model.TEXT_ICON_COLORS });
          const currentSite = (id) => space?.sites?.find?.((site) => site.id === id) || null;
          const currentCategory = (id) => space?.categories?.find?.((category) => category.id === id) || null;
          const currentTask = (id) => tasks.find((task) => task.id === id) || null;
          const currentTaskPage = (taskId, pageId) => currentTask(taskId)?.pages?.find?.((page) => page.id === pageId) || null;
          const chooseTaskReplacement = options.chooseTaskReplacement || (async ({ candidates }) => {
            const choices = candidates.map((candidate, index2) => `${index2 + 1}. ${candidate.title || candidate.id}`).join("\n");
            const selected = windowRef.prompt?.(`${copy.taskChooseReplacement}
${choices}`, "1");
            if (selected == null) return null;
            const index = Number.parseInt(String(selected).trim(), 10) - 1;
            return candidates[index]?.id || String(selected).trim();
          });
          function send(message) {
            try {
              const result = chromeApi.runtime.sendMessage?.(message);
              if (result && typeof result.then === "function") return result;
              return new Promise((resolve, reject) => chromeApi.runtime.sendMessage(message, (response) => {
                const error = chromeApi.runtime.lastError;
                if (error) reject(new Error(error.message));
                else resolve(response);
              }));
            } catch (error) {
              return Promise.reject(error);
            }
          }
          async function readSync(key) {
            try {
              const result = chromeApi.storage?.sync?.get?.(key);
              return result && typeof result.then === "function" ? await result : result || {};
            } catch (_error) {
              return {};
            }
          }
          function make(tag, className, text) {
            const node = documentRef.createElement(tag);
            if (className) node.className = className;
            if (text != null) node.textContent = text;
            return node;
          }
          function setFailure(failed, message) {
            if (!status) return;
            status.hidden = !failed;
            if (statusText) statusText.textContent = message || copy.failed;
          }
          function toast(message) {
            const node = el("toast");
            if (!node) return;
            windowRef.clearTimeout?.(toastTimer);
            node.textContent = message;
            node.hidden = false;
            toastTimer = windowRef.setTimeout?.(() => {
              node.hidden = true;
            }, 2200) || 0;
          }
          function applyCopy() {
            documentRef.documentElement.lang = language === "zh" ? "zh-CN" : "en";
            const text = {
              brandSubtitle: copy.subtitle,
              quickSearch: copy.search,
              openWorkspaceText: copy.open,
              sitesTitle: copy.sites,
              tasksTitle: copy.tasks,
              syncText: copy.local,
              addSiteText: copy.addSite,
              addCategoryText: copy.addCategory,
              taskEmptyTitle: copy.taskEmpty,
              taskEmptyOpen: copy.taskCreate,
              iconSizeLabel: copy.iconSize,
              titleSizeLabel: copy.titleSize,
              emptySitesTitle: copy.emptySites,
              emptySitesDescription: copy.emptySitesDescription,
              emptyAddCategory: copy.addCategory,
              newtabStatusText: copy.loadFailed,
              newtabRetry: copy.retry,
              siteDialogTitle: editingSiteId ? copy.editSite : copy.addSite,
              siteDialogDescription: copy.siteDescription,
              popularSearchInput: copy.popularSearch,
              bookmarkPermission: copy.bookmarkPermission,
              siteCancel: copy.cancel,
              categoryNameLabel: copy.categoryName,
              categoryCancel: copy.cancel,
              categoryDialogTitle: editingCategoryId ? copy.editCategory : copy.addCategory,
              categorySave: copy.saveCategory
            };
            Object.entries(text).forEach(([id, value]) => {
              const node = el(id);
              if (!node) return;
              if (node.tagName === "INPUT") node.placeholder = value;
              else node.textContent = value;
            });
            const tabs = el("siteDialogTabs")?.querySelectorAll?.("[data-add-tab]") || [];
            const tabCopy = { custom: copy.custom, popular: copy.popular, bookmarks: copy.bookmarks };
            tabs.forEach((button) => {
              button.textContent = tabCopy[button.dataset.addTab];
            });
            el("siteDialogTabs")?.setAttribute?.("aria-label", copy.addMethods);
            searchDialogRuntime?.refreshLanguage?.();
            if (el("siteSave")) el("siteSave").textContent = editingSiteId ? copy.saveSite : copy.addSite;
            const attributes = [
              ["topActions", "aria-label", copy.pageActions],
              ["quickSearch", "aria-label", copy.search],
              ["themeToggle", "aria-label", copy.theme],
              ["themeToggle", "title", copy.theme],
              ["settingsButton", "aria-label", copy.settings],
              ["settingsButton", "title", copy.settings],
              ["refreshTasks", "aria-label", copy.refreshTasks],
              ["refreshTasks", "title", copy.refreshTasks]
            ];
            attributes.forEach(([id, name, value]) => el(id)?.setAttribute?.(name, value));
            documentRef.querySelectorAll?.("[data-dialog-close]").forEach((button) => button.setAttribute("aria-label", copy.close));
          }
          function displayCategoryTitle(category) {
            const definition = model.DEFAULT_DEFINITION?.find?.((item) => item.id === category?.id);
            if (!definition || ![definition.zh, definition.en].includes(category?.title)) return category?.title || "";
            return language === "zh" ? definition.zh : definition.en;
          }
          function editorCopy() {
            return {
              category: copy.editorCategory,
              title: copy.editorTitle,
              url: copy.editorUrl,
              icon: copy.editorIcon,
              iconSource: copy.editorIconSource,
              iconAuto: copy.editorAuto,
              iconText: copy.editorText,
              iconUpload: copy.editorUpload,
              iconTextLabel: copy.editorTextLabel,
              iconTextPlaceholder: copy.editorTextPlaceholder,
              iconColor: copy.editorColor,
              uploadChoose: copy.editorUploadChoose,
              uploadHint: copy.editorUploadHint
            };
          }
          function markWorkspaceSurface() {
            const host = documentRef.querySelector?.(WORKSPACE_HOST_SELECTOR);
            if (!host) return false;
            if (host.dataset) host.dataset.tfaSurface = "newtab-overlay";
            else host.setAttribute?.("data-tfa-surface", "newtab-overlay");
            return true;
          }
          async function ensureLocalFeature(feature) {
            if (bootstrap.hasFeature?.(feature) || feature !== "web_dock") return true;
            if (localFeatureLoads.has(feature)) return localFeatureLoads.get(feature);
            const operation = new Promise((resolve) => {
              const script = documentRef.createElement?.("script");
              const parent = documentRef.head || documentRef.documentElement;
              if (!script || !parent?.appendChild || typeof chromeApi.runtime?.getURL !== "function") {
                resolve(false);
                return;
              }
              script.src = chromeApi.runtime.getURL("web_dock.js");
              script.async = true;
              script.addEventListener?.("load", () => resolve(bootstrap.hasFeature?.(feature) === true), { once: true });
              script.addEventListener?.("error", () => resolve(false), { once: true });
              parent.appendChild(script);
            }).finally(() => localFeatureLoads.delete(feature));
            localFeatureLoads.set(feature, operation);
            return operation;
          }
          async function requestFeature(feature, intent) {
            try {
              if (!await ensureLocalFeature(feature)) throw new Error("feature bundle unavailable");
              const opened = await bootstrap.requestFeature(feature, intent);
              if (opened === false) throw new Error("feature unavailable");
              markWorkspaceSurface();
              return true;
            } catch (_error) {
              toast(copy.failed);
              return false;
            }
          }
          function openWorkspace() {
            return requestFeature("workspace", { type: "toggle-command", source: "newtab-site-space" });
          }
          let settingsOpenPromise = null;
          function openSettings() {
            if (settingsOpenPromise) return settingsOpenPromise;
            const operation = requestFeature("web_dock", {
              type: "open-settings",
              section: "web-dock",
              source: "newtab-site-space"
            });
            const settled = operation.finally(() => {
              if (settingsOpenPromise === settled) settingsOpenPromise = null;
            });
            settingsOpenPromise = settled;
            return settled;
          }
          function iconColor(site) {
            return model.TEXT_ICON_COLORS.find((item) => item.id === site.iconColor)?.hex || model.TEXT_ICON_COLORS[0]?.hex || "#7164e8";
          }
          function catalogSiteFor(site) {
            return catalog?.findByUrl?.(site.url) || catalog?.sites?.find?.((item) => item.id === site.id) || null;
          }
          function siteIcon(site, sizeClass = "") {
            const icon = make("span", `site-icon ${sizeClass}`.trim());
            const catalogSite = catalogSiteFor(site);
            if (site.iconKind === "letter") {
              icon.style.background = iconColor(site);
              icon.textContent = (site.iconValue || site.title || "?").trim().slice(0, 1).toUpperCase();
              return icon;
            }
            const image = make("img");
            image.alt = "";
            image.decoding = "async";
            if (site.favicon) image.src = site.favicon;
            else if (catalogSite?.iconFile) image.src = catalogSite.iconFile;
            else if (site.iconKind === "catalog" && site.iconValue) image.src = site.iconValue;
            else image.src = `chrome-extension://${chromeApi.runtime.id}/_favicon/?pageUrl=${encodeURIComponent(site.url)}&size=64`;
            image.addEventListener?.("error", () => {
              image.remove?.();
              icon.style.background = iconColor(site);
              icon.textContent = (site.title || "?").slice(0, 1).toUpperCase();
            }, { once: true });
            icon.append?.(image);
            return icon;
          }
          function actionButton(label, iconName, className, handler) {
            const button = make("button", `site-action ${className || ""}`.trim());
            button.type = "button";
            button.title = label;
            button.setAttribute("aria-label", label);
            button.innerHTML = ICONS[iconName] || "";
            button.addEventListener("click", (event) => {
              event.stopPropagation();
              handler(event);
            });
            return button;
          }
          function closeDialog(dialog, restoreFocus = null) {
            if (!dialog?.open) return false;
            dialog.close();
            if (restoreFocus?.focus) windowRef.setTimeout?.(() => {
              restoreFocus.focus();
            }, 0);
            return true;
          }
          async function togglePin(site, button) {
            button.disabled = true;
            try {
              const response = await send({ type: "DOCK_TOGGLE_SITE", site: { title: site.title, url: site.url } });
              if (!response?.ok) throw new Error(response?.message || copy.failed);
              if (response.fixed) dockUrls.add(site.url);
              else dockUrls.delete(site.url);
              renderSites();
              toast(response.fixed ? copy.pinned : copy.unpinned);
            } catch (error) {
              toast(error.message || copy.failed);
            } finally {
              button.disabled = false;
            }
          }
          function captureDragLayouts(...containers) {
            const layouts = /* @__PURE__ */ new Map();
            containers.forEach((container) => {
              if (container && !layouts.has(container)) layouts.set(container, captureLayout(container));
            });
            return layouts;
          }
          function animateDragLayouts(layouts) {
            layouts.forEach((previous, container) => animateLayout(container, previous));
          }
          function beginDrag(payload) {
            if (dragRefreshFlushTimer) windowRef.clearTimeout?.(dragRefreshFlushTimer);
            dragRefreshFlushTimer = 0;
            dragRenderGuard = true;
            dragPayload = payload;
          }
          function taskRenderBlocked() {
            return dragRenderGuard || Boolean(dragPayload) || documentRef.documentElement?.classList?.contains?.("is-site-space-dragging");
          }
          function queueTaskRefreshAfterDrag() {
            if (!dragRenderGuard || dragPayload || destroyed) return;
            if (dragRefreshFlushTimer) windowRef.clearTimeout?.(dragRefreshFlushTimer);
            dragRefreshFlushTimer = windowRef.setTimeout?.(() => {
              dragRefreshFlushTimer = 0;
              dragRenderGuard = false;
              flushDeferredTaskRefresh();
            }, 320) || 0;
          }
          function removeDragPlaceholder() {
            if (!dragPlaceholder) return;
            const parent = dragPlaceholder.parentElement;
            const layouts = captureDragLayouts(parent);
            dragPlaceholder.remove?.();
            dragPlaceholder = null;
            animateDragLayouts(layouts);
          }
          function clearDragPresentation() {
            documentRef.documentElement?.classList?.remove?.("is-site-space-dragging");
            documentRef.querySelectorAll?.(".is-dragging,.is-drop-target,.is-drag-over,.is-task-drop-target,.is-page-drop-before,.is-page-drop-after")?.forEach?.((node) => {
              node.classList.remove("is-dragging", "is-drop-target", "is-drag-over", "is-task-drop-target", "is-page-drop-before", "is-page-drop-after");
              delete node.dataset.dropSide;
            });
            removeDragPlaceholder();
            queueTaskRefreshAfterDrag();
          }
          function dropPayload(event) {
            if (dragPayload) return { ...dragPayload };
            try {
              return decodeDragPayload(event?.dataTransfer?.getData?.("text/plain"));
            } catch (_error) {
              return null;
            }
          }
          function placeDragPlaceholder(type, container, target = null, side = "before", metadata = {}) {
            if (!container) return null;
            const oldParent = dragPlaceholder?.parentElement || null;
            const replacing = !dragPlaceholder || dragPlaceholder.dataset.placeholderType !== type;
            const reference = target ? side === "after" ? dragPlaceholder && target.nextSibling === dragPlaceholder ? dragPlaceholder.nextSibling : target.nextSibling : target : null;
            const repositioning = replacing || oldParent !== container || dragPlaceholder.nextSibling !== reference;
            const layouts = repositioning ? captureDragLayouts(oldParent, container) : null;
            if (replacing) {
              dragPlaceholder?.remove?.();
              dragPlaceholder = make(type === "category" ? "article" : "div", `drag-placeholder is-${type}`);
              dragPlaceholder.dataset.placeholderType = type;
              dragPlaceholder.setAttribute("aria-hidden", "true");
              dragPlaceholder.addEventListener("dragover", (event) => {
                if (!dropPayload(event)) return;
                event.preventDefault();
                event.stopPropagation();
                if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
              });
              dragPlaceholder.addEventListener("drop", async (event) => {
                const currentPayload = dropPayload(event);
                if (!currentPayload) return;
                event.preventDefault();
                event.stopPropagation();
                const payload = { ...currentPayload };
                const placeholderType = dragPlaceholder.dataset.placeholderType;
                const targetId = dragPlaceholder.dataset.targetId || "";
                const categoryId = dragPlaceholder.dataset.dropCategoryId || "";
                const taskId = dragPlaceholder.dataset.taskId || "";
                const dropAfter = dragPlaceholder.dataset.dropSide === "after";
                dragPayload = null;
                clearDragPresentation();
                if (placeholderType === "site" && payload.type === "site") {
                  if (targetId) await moveSiteRelative(payload.id, targetId, dropAfter ? "after" : "before");
                  else if (categoryId) await moveSite(payload.id, categoryId);
                } else if (placeholderType === "category" && payload.type === "category" && targetId) {
                  await moveCategoryRelative(payload.id, targetId, dropAfter ? "after" : "before");
                } else if (placeholderType === "task-page" && payload.type === "task-page" && taskId) {
                  await moveTaskPage(payload.sourceTaskId, taskId, payload.pageId, targetId || null, dropAfter);
                }
              });
            }
            dragPlaceholder.dataset.targetId = metadata.targetId || "";
            dragPlaceholder.dataset.dropCategoryId = metadata.categoryId || "";
            dragPlaceholder.dataset.taskId = metadata.taskId || "";
            dragPlaceholder.dataset.dropSide = side;
            if (repositioning) {
              container.insertBefore?.(dragPlaceholder, reference);
              animateDragLayouts(layouts);
            }
            return dragPlaceholder;
          }
          function dropSideFor(event, node) {
            const rect = node?.getBoundingClientRect?.();
            if (!rect) return "before";
            const sameRowBias = Math.abs(Number(event?.clientY) - (rect.top + rect.height / 2)) <= rect.height * 0.42;
            return sameRowBias ? Number(event?.clientX) > rect.left + rect.width * 0.58 ? "after" : "before" : Number(event?.clientY) >= rect.top + rect.height / 2 ? "after" : "before";
          }
          function createSiteNode(siteId) {
            const item = make("div", "site-item");
            item.tabIndex = 0;
            item.dataset.siteId = siteId;
            item.draggable = true;
            item.setAttribute("role", "link");
            item.append(make("span", "site-title"), make("span", "site-actions"));
            const open = () => {
              const site = currentSite(item.dataset.siteId);
              if (site) windowRef.location?.assign?.(site.url);
            };
            item.addEventListener("click", (event) => {
              if (Date.now() < suppressSiteClickUntil || event.target?.closest?.(".site-actions")) return;
              open();
            });
            item.addEventListener("keydown", (event) => {
              if (event.key === "Enter" || event.key === " ") {
                event.preventDefault();
                open();
              }
            });
            item.addEventListener("dragstart", (event) => {
              const site = currentSite(item.dataset.siteId);
              if (!site) {
                event.preventDefault();
                return;
              }
              beginDrag({ type: "site", id: site.id });
              documentRef.documentElement?.classList?.add?.("is-site-space-dragging");
              item.classList.add("is-dragging");
              event.dataTransfer?.setData("text/plain", `site:${site.id}`);
              if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
            });
            item.addEventListener("dragend", () => {
              suppressSiteClickUntil = Date.now() + 180;
              dragPayload = null;
              clearDragPresentation();
            });
            item.addEventListener("dragover", (event) => {
              const siteId2 = item.dataset.siteId;
              const payload = dropPayload(event);
              if (payload?.type === "site" && payload.id !== siteId2) {
                event.preventDefault();
                event.stopPropagation();
                item.classList.add("is-drop-target");
                item.dataset.dropSide = dropSideFor(event, item);
                placeDragPlaceholder("site", item.parentElement, item, item.dataset.dropSide, { targetId: siteId2 });
              }
            });
            item.addEventListener("dragleave", () => {
              item.classList.remove("is-drop-target");
              delete item.dataset.dropSide;
            });
            item.addEventListener("drop", async (event) => {
              const targetId = item.dataset.siteId;
              const payload = dropPayload(event);
              if (payload?.type !== "site" || payload.id === targetId) return;
              event.preventDefault();
              event.stopPropagation();
              const side = item.dataset.dropSide === "after" ? "after" : "before";
              const sourceId = payload.id;
              const pending = moveSiteRelative(sourceId, targetId, side);
              dragPayload = null;
              clearDragPresentation();
              await pending;
            });
            return item;
          }
          function syncSiteNode(item, site) {
            item.dataset.siteId = site.id;
            item.setAttribute("aria-label", site.title);
            const signature = JSON.stringify([site.title, site.url, site.iconKind, site.iconValue, site.iconColor, site.favicon || ""]);
            let title = item.querySelector?.(".site-title");
            if (!title) {
              title = make("span", "site-title");
              item.append(title);
            }
            title.textContent = site.title;
            if (item.dataset.visualSignature !== signature || !item.querySelector?.(".site-icon")) {
              const icon = siteIcon(site);
              const existing = item.querySelector?.(".site-icon");
              if (existing) existing.replaceWith(icon);
              else item.insertBefore(icon, title);
              item.dataset.visualSignature = signature;
            }
            let actions = item.querySelector?.(".site-actions");
            if (!actions) {
              actions = make("span", "site-actions");
              item.append(actions);
            }
            const fixed = dockUrls.has(site.url);
            let pin;
            pin = actionButton(fixed ? copy.unpin : copy.pin, "pin", fixed ? "is-fixed" : "", () => togglePin(currentSite(site.id) || site, pin));
            actions.replaceChildren(
              actionButton(copy.editSite, "edit", "", () => openSiteDialog(currentSite(site.id) || site)),
              pin,
              actionButton(copy.deleteSite, "delete", "danger", () => removeSite(site.id))
            );
            return item;
          }
          function createCategoryNode(categoryId) {
            const card = make("article", "category-card");
            card.dataset.categoryId = categoryId;
            const header = make("header", "category-header");
            header.draggable = true;
            const title = make("button", "category-title-button");
            title.type = "button";
            title.append(make("h2"));
            title.addEventListener("click", () => openCategoryDialog(currentCategory(card.dataset.categoryId)));
            header.append(title, make("span", "category-count"));
            const actions = make("span", "category-actions");
            header.append(actions);
            const grid = make("div", "site-grid");
            const empty = make("span", "empty-category");
            card.append(header, grid, empty);
            header.addEventListener("dragstart", (event) => {
              if (event.target?.closest?.(".category-actions")) {
                event.preventDefault();
                return;
              }
              const categoryIdValue = card.dataset.categoryId;
              beginDrag({ type: "category", id: categoryIdValue });
              documentRef.documentElement?.classList?.add?.("is-site-space-dragging");
              card.classList.add("is-dragging");
              event.dataTransfer?.setData("text/plain", `category:${categoryIdValue}`);
              if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
            });
            header.addEventListener("dragend", () => {
              dragPayload = null;
              clearDragPresentation();
            });
            card.addEventListener("dragover", (event) => {
              const categoryIdValue = card.dataset.categoryId;
              const payload = dropPayload(event);
              if (payload?.type === "site" || payload?.type === "category" && payload.id !== categoryIdValue) {
                event.preventDefault();
                card.classList.add("is-drag-over");
                if (payload.type === "category") {
                  card.dataset.dropSide = dropSideFor(event, card);
                  placeDragPlaceholder("category", board, card, card.dataset.dropSide, { targetId: categoryIdValue });
                } else {
                  placeDragPlaceholder("site", grid, null, "after", { categoryId: categoryIdValue });
                }
              }
            });
            card.addEventListener("dragleave", (event) => {
              if (!card.contains?.(event.relatedTarget)) {
                card.classList.remove("is-drag-over");
                delete card.dataset.dropSide;
              }
            });
            card.addEventListener("drop", async (event) => {
              event.preventDefault();
              const categoryIdValue = card.dataset.categoryId;
              const payload = dropPayload(event);
              if (!payload || payload.id === categoryIdValue) return;
              const side = card.dataset.dropSide === "after" ? "after" : "before";
              const pending = payload.type === "site" ? moveSite(payload.id, categoryIdValue) : moveCategoryRelative(payload.id, categoryIdValue, side);
              dragPayload = null;
              clearDragPresentation();
              await pending;
            });
            return card;
          }
          function syncCategoryNode(card, category, siteNodes) {
            card.dataset.categoryId = category.id;
            const sites = space.sites.filter((site) => site.categoryId === category.id).sort((a, b) => a.order - b.order);
            const title = card.querySelector?.(".category-title-button");
            title.dataset.editHint = copy.editHint;
            title.querySelector?.("h2")?.replaceChildren?.(displayCategoryTitle(category));
            const count = card.querySelector?.(".category-count");
            if (count) count.textContent = String(sites.length);
            const actions = card.querySelector?.(".category-actions");
            actions?.replaceChildren(
              actionButton(copy.addSite, "add", "", () => openSiteDialog(null, category.id)),
              actionButton(copy.deleteSite, "delete", "danger", () => removeCategory(category.id))
            );
            const grid = card.querySelector?.(".site-grid");
            sites.forEach((site) => {
              const item = syncSiteNode(siteNodes.get(site.id) || createSiteNode(site.id), site);
              siteNodes.set(site.id, item);
              grid.append(item);
            });
            const empty = card.querySelector?.(".empty-category");
            if (empty) empty.textContent = copy.emptyDrop;
            return card;
          }
          function applyDisplayPreferences() {
            if (!space) return;
            const display = model.normalizeDisplay?.(pendingDisplay || space.display) || pendingDisplay || space.display || { iconSize: 52, titleSize: 12 };
            documentRef.documentElement?.style?.setProperty?.("--site-icon-size", `${display.iconSize}px`);
            documentRef.documentElement?.style?.setProperty?.("--site-title-size", `${display.titleSize}px`);
            if (el("iconSizeRange")) el("iconSizeRange").value = String(display.iconSize);
            if (el("titleSizeRange")) el("titleSizeRange").value = String(display.titleSize);
          }
          function renderSites({ animate = false } = {}) {
            if (!board || !space) return;
            const previous = animate ? captureBoardLayout() : null;
            applyDisplayPreferences();
            const categoryNodes = new Map([...board.querySelectorAll?.("[data-category-id]") || []].map((node) => [node.dataset.categoryId, node]));
            const siteNodes = new Map([...board.querySelectorAll?.("[data-site-id]") || []].map((node) => [node.dataset.siteId, node]));
            const categories = [...space.categories].sort((a, b) => a.order - b.order);
            categories.forEach((category) => {
              const card = syncCategoryNode(categoryNodes.get(category.id) || createCategoryNode(category.id), category, siteNodes);
              categoryNodes.delete(category.id);
              board.append(card);
            });
            categoryNodes.forEach((node) => node.remove?.());
            const activeSiteIds = new Set(space.sites.map((site) => site.id));
            siteNodes.forEach((node, id) => {
              if (!activeSiteIds.has(id)) node.remove?.();
            });
            if (el("emptyState")) el("emptyState").hidden = categories.length > 0;
            board.hidden = categories.length === 0;
            if (previous) animateBoardLayout(previous);
          }
          function captureBoardLayout() {
            return captureLayout(board);
          }
          function captureLayout(container) {
            const positions = /* @__PURE__ */ new Map();
            container?.querySelectorAll?.(":scope > [data-category-id],:scope > [data-site-id],:scope > [data-task-page-id]")?.forEach?.((node) => {
              const key = node.dataset.siteId ? `site:${node.dataset.siteId}` : `category:${node.dataset.categoryId}`;
              const resolvedKey = node.dataset.taskPageId ? `task-page:${node.dataset.taskId}:${node.dataset.taskPageId}` : key;
              positions.set(resolvedKey, node.getBoundingClientRect());
            });
            return positions;
          }
          function animateBoardLayout(previous) {
            animateLayout(board, previous);
          }
          function animateLayout(container, previous) {
            if (windowRef.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches) return;
            const run = () => container?.querySelectorAll?.(":scope > [data-category-id],:scope > [data-site-id],:scope > [data-task-page-id]")?.forEach?.((node) => {
              const key = node.dataset.siteId ? `site:${node.dataset.siteId}` : `category:${node.dataset.categoryId}`;
              const resolvedKey = node.dataset.taskPageId ? `task-page:${node.dataset.taskId}:${node.dataset.taskPageId}` : key;
              const before = previous.get(resolvedKey);
              const after = node.getBoundingClientRect?.();
              if (!before || !after || typeof node.animate !== "function") return;
              const x = before.left - after.left;
              const y = before.top - after.top;
              if (Math.abs(x) < 1 && Math.abs(y) < 1) return;
              layoutAnimations.get(node)?.cancel?.();
              const distance = Math.hypot(x, y);
              const animation = node.animate([
                { transform: `translate3d(${x}px, ${y}px, 0) scale(.985)`, opacity: 0.82, zIndex: 3 },
                { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1, zIndex: 3 }
              ], {
                duration: Math.max(180, Math.min(300, Math.round(180 + distance * 0.16))),
                easing: "cubic-bezier(.16,1,.3,1)"
              });
              layoutAnimations.set(node, animation);
              animation?.finished?.catch?.(() => {
              }).finally?.(() => {
                if (layoutAnimations.get(node) === animation) layoutAnimations.delete(node);
              });
            });
            if (typeof windowRef.requestAnimationFrame === "function") windowRef.requestAnimationFrame(run);
            else windowRef.setTimeout?.(run, 0);
          }
          const statusLabel = (statusValue) => ({
            active: copy.taskActive,
            paused: copy.taskPaused,
            done: copy.taskDone,
            pending: copy.taskPending
          })[statusValue] || copy.taskActive;
          const domainLabel = (domain) => ({
            work: copy.domainWork,
            study: copy.domainStudy,
            life: copy.domainLife,
            interest: copy.domainInterest,
            admin: copy.domainAdmin,
            other: copy.domainOther
          })[domain] || copy.domainOther;
          const taskColor = (domain) => ({
            work: "#7164e8",
            study: "#4b82c8",
            life: "#45a67c",
            interest: "#c17551",
            admin: "#b08a3e",
            other: "#818795"
          })[domain] || "#818795";
          const TASK_DOMAINS = Object.freeze(["work", "study", "life", "interest", "admin", "other"]);
          const TASK_STATUSES = Object.freeze(["active", "paused", "done"]);
          const hostOf = (value) => {
            try {
              return new URL(model.safeUrl(value)).hostname.replace(/^www\./, "");
            } catch (_error) {
              return "";
            }
          };
          async function activateTaskPage(page) {
            const url = model.safeUrl(page?.url);
            if (!url) return false;
            const openTab = openTabs.find((tab) => model.safeUrl(tab.url) === url);
            const response = openTab && Number.isInteger(Number(openTab.id)) ? await send({ type: "SWITCH_TAB", tabId: Number(openTab.id) }) : await send({ type: "NEW_TAB", url, active: true });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              return false;
            }
            await loadTasks();
            return true;
          }
          async function removeTaskPage(task, page) {
            const candidates = page?.relation?.isPrimary ? replacementTaskCandidates(tasks, task.id, page.id) : [];
            const replacementTaskId = page?.relation?.isPrimary ? await resolveReplacementTaskId(candidates, (choices) => chooseTaskReplacement({
              task,
              page,
              candidates: choices.map((candidate) => ({ id: candidate.id, title: candidate.title }))
            })) : "";
            if (replacementTaskId == null) {
              toast(copy.taskReplacementCancelled);
              return false;
            }
            const response = await send({
              type: "V2_RELATION_REMOVE",
              taskId: task.id,
              pageId: page.id,
              ...replacementTaskId ? { replacementTaskId } : {}
            });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              return false;
            }
            await loadTasks();
            toast(copy.taskPageRemoved);
            return true;
          }
          async function assignSiteToTask(siteId, taskId) {
            const site = currentSite(siteId);
            if (!site) return false;
            const response = await send({
              type: "V2_RELATION_ADD",
              taskId,
              page: { title: site.title, url: site.url, restoreUrl: site.url }
            });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              return false;
            }
            await loadTasks();
            toast(copy.siteAssigned);
            return true;
          }
          async function moveTaskPage(sourceTaskId, targetTaskId, pageId, targetPageId = null, after = false) {
            if (sourceTaskId === targetTaskId) {
              const task = tasks.find((item) => item.id === sourceTaskId);
              const original = (task?.pages || []).map((page) => page.id);
              const orderedPageIds = reorderIds(original, pageId, targetPageId, after);
              if (orderedPageIds.every((id, index) => id === original[index])) return true;
              const response2 = await send({ type: "V2_RELATION_REORDER", taskId: sourceTaskId, orderedPageIds });
              if (!response2?.ok) {
                toast(response2?.message || copy.failed);
                await loadTasks();
                return false;
              }
              await loadTasks();
              toast(copy.taskPageReordered);
              return true;
            }
            const target = tasks.find((task) => task.id === targetTaskId);
            const orderedTargetPageIds = insertIdRelative(
              (target?.pages || []).map((page) => page.id),
              pageId,
              targetPageId,
              after
            );
            const response = await send({
              type: "V2_RELATION_MOVE",
              sourceTaskId,
              targetTaskId,
              pageId,
              orderedTargetPageIds
            });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              await loadTasks();
              return false;
            }
            await loadTasks();
            toast(copy.taskPageMoved);
            return true;
          }
          function taskPageIsOpen(page) {
            const url = model.safeUrl(page?.url);
            return Boolean(url && openTabs.some((tab) => model.safeUrl(tab.url) === url));
          }
          function createTaskPageNode(taskId, pageId) {
            const card = make("article", "task-page-card");
            card.dataset.taskPageId = pageId;
            card.dataset.taskId = taskId;
            const main = make("div", "task-page-main");
            main.setAttribute("role", "button");
            main.tabIndex = 0;
            main.draggable = true;
            const content = make("span", "task-page-copy");
            content.append(make("strong"), make("small"), make("span", "task-page-semantics"));
            main.append(make("span", "site-icon task-tab-icon"), content);
            const actions = make("span", "task-page-actions");
            card.append(main, actions);
            main.addEventListener("click", () => {
              const page = currentTaskPage(card.dataset.taskId, card.dataset.taskPageId);
              if (page) activateTaskPage(page);
            });
            main.addEventListener("keydown", (event) => {
              if (event.key !== "Enter" && event.key !== " ") return;
              event.preventDefault();
              const page = currentTaskPage(card.dataset.taskId, card.dataset.taskPageId);
              if (page) activateTaskPage(page);
            });
            main.addEventListener("dragstart", (event) => {
              const taskIdValue = card.dataset.taskId;
              const pageIdValue = card.dataset.taskPageId;
              if (!currentTaskPage(taskIdValue, pageIdValue)) {
                event.preventDefault();
                return;
              }
              beginDrag({ type: "task-page", pageId: pageIdValue, sourceTaskId: taskIdValue });
              documentRef.documentElement?.classList?.add?.("is-site-space-dragging");
              card.classList.add("is-dragging");
              event.dataTransfer?.setData("text/plain", `task-page:${taskIdValue}:${pageIdValue}`);
              if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
            });
            main.addEventListener("dragend", () => {
              dragPayload = null;
              clearDragPresentation();
            });
            card.addEventListener("dragover", (event) => {
              const payload = dropPayload(event);
              const pageIdValue = card.dataset.taskPageId;
              if (payload?.type !== "task-page" || payload.pageId === pageIdValue) return;
              event.preventDefault();
              event.stopPropagation();
              const side = dropSideFor(event, card);
              card.classList.toggle("is-page-drop-before", side !== "after");
              card.classList.toggle("is-page-drop-after", side === "after");
              placeDragPlaceholder("task-page", card.parentElement, card, side, {
                targetId: pageIdValue,
                taskId: card.dataset.taskId
              });
            });
            card.addEventListener("drop", async (event) => {
              const payload = dropPayload(event);
              const pageIdValue = card.dataset.taskPageId;
              if (payload?.type !== "task-page" || payload.pageId === pageIdValue) return;
              event.preventDefault();
              event.stopPropagation();
              const targetTaskId = card.dataset.taskId;
              const after = card.classList.contains("is-page-drop-after");
              dragPayload = null;
              clearDragPresentation();
              await moveTaskPage(payload.sourceTaskId, targetTaskId, payload.pageId, pageIdValue, after);
            });
            return card;
          }
          function syncTaskPageNode(card, task, page) {
            card.dataset.taskPageId = page.id;
            card.dataset.taskId = task.id;
            const opened = taskPageIsOpen(page);
            card.classList.toggle("is-open", opened);
            const main = card.querySelector?.(".task-page-main");
            if (main) main.title = copy.taskPageOpen;
            const signature = JSON.stringify([page.title, page.url, page.iconKind, page.iconValue, page.iconColor, page.favicon || ""]);
            if (main && (card.dataset.visualSignature !== signature || !main.querySelector?.(".site-icon"))) {
              const icon = siteIcon(page, "task-tab-icon");
              const existing = main.querySelector?.(".site-icon");
              if (existing) existing.replaceWith(icon);
              else main.prepend?.(icon);
              card.dataset.visualSignature = signature;
            }
            const content = card.querySelector?.(".task-page-copy");
            const title = content?.querySelector?.("strong");
            const url = content?.querySelector?.("small");
            if (title) title.textContent = page.title || hostOf(page.url) || page.url;
            if (url) url.textContent = hostOf(page.url) || page.url;
            const semantics = make("span", "task-page-semantics");
            const smart = ["ai", "rule"].includes(page.relation?.source);
            semantics.append(
              make("span", `task-page-open-state${opened ? " is-open" : ""}`, opened ? copy.taskPageOpened : copy.taskPageClosed),
              make("span", `task-page-source is-${smart ? "smart" : "manual"}`, smart ? copy.taskSourceSmart : copy.taskSourceManual)
            );
            if (page.relation?.isPrimary) semantics.append(make("span", "task-page-primary", copy.taskPrimary));
            content?.querySelector?.(".task-page-semantics")?.replaceWith?.(semantics);
            const actions = card.querySelector?.(".task-page-actions");
            actions?.replaceChildren(
              actionButton(copy.taskPageOpen, "open", "", () => {
                const current = currentTaskPage(card.dataset.taskId, card.dataset.taskPageId);
                if (current) activateTaskPage(current);
              }),
              actionButton(copy.taskPageRemove, "delete", "danger", () => {
                const currentTaskValue = currentTask(card.dataset.taskId);
                const currentPageValue = currentTaskPage(card.dataset.taskId, card.dataset.taskPageId);
                if (currentTaskValue && currentPageValue) removeTaskPage(currentTaskValue, currentPageValue);
              })
            );
            return card;
          }
          function createTaskNode(taskId) {
            const card = make("article", "task-card");
            card.dataset.taskId = taskId;
            const header = make("header", "task-card-header");
            const rename = make("button", "task-title");
            rename.type = "button";
            rename.append(make("strong"), make("small"));
            rename.addEventListener("click", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) renameTask(task);
            });
            const domain = make("select", "task-domain");
            domain.addEventListener("change", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) updateTaskField(task, { domain: domain.value }, copy.taskDomainUpdated);
            });
            const status2 = make("select", "task-status");
            status2.addEventListener("change", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) updateTaskField(task, { status: status2.value }, copy.taskUpdated);
            });
            header.append(rename, domain, status2);
            const pages = make("div", "task-page-list");
            const actions = make("div", "task-actions");
            const open = make("button", "task-open");
            open.type = "button";
            open.addEventListener("click", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) openTask(task);
            });
            const close = make("button", "task-close");
            close.type = "button";
            close.addEventListener("click", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) closeTask(task);
            });
            const moreWrap = make("span", "task-more-wrap");
            const more = make("button", "task-more");
            more.type = "button";
            more.setAttribute("aria-haspopup", "menu");
            more.setAttribute("aria-expanded", "false");
            const menu = make("span", "task-more-menu");
            menu.hidden = true;
            menu.setAttribute("role", "menu");
            const dissolve = make("button", "is-danger");
            dissolve.type = "button";
            dissolve.setAttribute("role", "menuitem");
            dissolve.addEventListener("click", () => {
              const task = currentTask(card.dataset.taskId);
              if (task) dissolveTask(task);
            });
            menu.append(dissolve);
            more.addEventListener("click", () => {
              menu.hidden = !menu.hidden;
              more.setAttribute("aria-expanded", String(!menu.hidden));
            });
            moreWrap.append(more, menu);
            actions.append(open, close, moreWrap);
            card.append(header, pages, actions);
            card.addEventListener("dragover", (event) => {
              const payload = dropPayload(event);
              if (payload?.type !== "site" && payload?.type !== "task-page") return;
              const taskIdValue = card.dataset.taskId;
              if (payload.type === "task-page" && payload.sourceTaskId === taskIdValue) return;
              event.preventDefault();
              card.classList.add("is-task-drop-target");
              if (payload.type === "task-page") placeDragPlaceholder("task-page", pages, null, "after", { taskId: taskIdValue });
              if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
            });
            card.addEventListener("dragleave", (event) => {
              if (!card.contains?.(event.relatedTarget)) card.classList.remove("is-task-drop-target");
            });
            card.addEventListener("drop", async (event) => {
              const payload = dropPayload(event);
              if (payload?.type !== "site" && payload?.type !== "task-page") return;
              event.preventDefault();
              const taskIdValue = card.dataset.taskId;
              dragPayload = null;
              clearDragPresentation();
              if (payload.type === "site") await assignSiteToTask(payload.id, taskIdValue);
              else if (payload.sourceTaskId !== taskIdValue) await moveTaskPage(payload.sourceTaskId, taskIdValue, payload.pageId);
            });
            return card;
          }
          function syncTaskNode(card, task) {
            card.dataset.taskId = task.id;
            card.dataset.taskStatus = task.status;
            card.classList.toggle("is-done", task.status === "done");
            card.style.setProperty("--task-color", taskColor(task.domain));
            const rename = card.querySelector?.(".task-title");
            if (rename) {
              rename.dataset.taskRename = task.id;
              rename.title = copy.taskRename;
              rename.setAttribute("aria-label", `${copy.taskRename}: ${task.title}`);
              const title = rename.querySelector?.("strong");
              const hint = rename.querySelector?.("small");
              if (title) title.textContent = task.title;
              if (hint) hint.textContent = copy.taskRename;
            }
            const domain = card.querySelector?.(".task-domain");
            if (domain) {
              domain.dataset.taskDomain = task.id;
              domain.setAttribute("aria-label", domainLabel(task.domain));
              domain.replaceChildren();
              TASK_DOMAINS.forEach((value) => {
                const option = make("option", "", domainLabel(value));
                option.value = value;
                option.selected = value === task.domain;
                domain.append(option);
              });
            }
            const status2 = card.querySelector?.(".task-status");
            if (status2) {
              status2.dataset.taskStatus = task.id;
              status2.setAttribute("aria-label", copy.taskStatusChange);
              status2.replaceChildren();
              TASK_STATUSES.forEach((value) => {
                const option = make("option", "", statusLabel(value));
                option.value = value;
                option.selected = value === task.status;
                status2.append(option);
              });
            }
            const pages = card.querySelector?.(".task-page-list");
            const pageNodes = new Map([...pages?.querySelectorAll?.(":scope > [data-task-page-id]") || []].map((node) => [node.dataset.taskPageId, node]));
            (task.pages || []).forEach((page) => {
              const node = syncTaskPageNode(pageNodes.get(page.id) || createTaskPageNode(task.id, page.id), task, page);
              pageNodes.delete(page.id);
              pages?.append?.(node);
            });
            pageNodes.forEach((node) => node.remove?.());
            let empty = pages?.querySelector?.(":scope > .task-drop-hint");
            if (!(task.pages || []).length) {
              if (!empty) {
                empty = make("div", "task-drop-hint");
                pages?.append?.(empty);
              }
              empty.textContent = copy.taskDropSite;
            } else {
              empty?.remove?.();
            }
            const open = card.querySelector?.(".task-open");
            if (open) {
              open.textContent = copy.taskOpen;
              open.dataset.taskOpen = task.id;
            }
            const close = card.querySelector?.(".task-close");
            if (close) {
              close.textContent = copy.taskClose;
              close.dataset.taskClose = task.id;
              close.disabled = !(task.pages || []).some((page) => taskPageIsOpen(page));
            }
            const more = card.querySelector?.(".task-more");
            if (more) {
              more.textContent = copy.taskMore;
              more.dataset.taskMore = task.id;
            }
            const dissolve = card.querySelector?.(".task-more-menu .is-danger");
            if (dissolve) {
              dissolve.textContent = copy.taskDissolve;
              dissolve.dataset.taskDissolve = task.id;
            }
            return card;
          }
          function renderTasks({ force = false } = {}) {
            if (!force && taskRenderBlocked()) {
              taskRenderPending = true;
              return false;
            }
            const list = el("taskList");
            if (!list) return false;
            const taskNodes = new Map([...list.querySelectorAll?.(":scope > .task-card[data-task-id]") || []].map((node) => [node.dataset.taskId, node]));
            tasks.forEach((task) => {
              const card = syncTaskNode(taskNodes.get(task.id) || createTaskNode(task.id), task);
              taskNodes.delete(task.id);
              list.append(card);
            });
            taskNodes.forEach((node) => node.remove?.());
            if (el("taskEmpty")) el("taskEmpty").hidden = tasks.length > 0;
            list.hidden = tasks.length === 0;
            taskRenderPending = false;
            return true;
          }
          function taskDropTarget(event) {
            const target = event?.target;
            const taskNode = target?.closest?.("[data-task-id]");
            if (!taskNode) return null;
            const taskId = String(taskNode.dataset.taskId || "");
            if (!taskId) return null;
            const pageNode = target?.closest?.("[data-task-page-id]");
            return {
              taskId,
              pageId: String(pageNode?.dataset?.taskPageId || ""),
              pageNode: pageNode || null
            };
          }
          function onTaskDragOverCapture(event) {
            const payload = dropPayload(event);
            if (payload?.type !== "task-page" && payload?.type !== "site") return;
            const target = taskDropTarget(event);
            if (!target) return;
            if (payload.type === "task-page") {
              if (payload.pageId === target.pageId) return;
              if (!target.pageId && payload.sourceTaskId === target.taskId) return;
            }
            event.preventDefault();
            if (payload.type === "site") removeDragPlaceholder();
            lastTaskDragDiagnostic = {
              phase: "dragover",
              payload: { ...payload },
              target: { taskId: target.taskId, pageId: target.pageId },
              at: Date.now()
            };
            if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
          }
          function onTaskDropCapture(event) {
            const payload = dropPayload(event);
            if (payload?.type !== "task-page" && payload?.type !== "site") return;
            const target = taskDropTarget(event);
            if (!target) return;
            if (payload.type === "task-page") {
              if (payload.pageId === target.pageId) return;
              if (!target.pageId && payload.sourceTaskId === target.taskId) return;
            }
            event.preventDefault();
            event.stopImmediatePropagation();
            const after = target.pageNode ? dropSideFor(event, target.pageNode) === "after" : false;
            dragPayload = null;
            clearDragPresentation();
            const diagnostic = {
              phase: "drop",
              payload: { ...payload },
              target: { taskId: target.taskId, pageId: target.pageId, after },
              at: Date.now()
            };
            lastTaskDragDiagnostic = diagnostic;
            const operation = payload.type === "site" ? assignSiteToTask(payload.id, target.taskId) : moveTaskPage(
              payload.sourceTaskId,
              target.taskId,
              payload.pageId,
              target.pageId || null,
              after
            );
            void Promise.resolve(operation).then(
              (ok) => {
                if (lastTaskDragDiagnostic === diagnostic) diagnostic.result = Boolean(ok);
              },
              (error) => {
                if (lastTaskDragDiagnostic === diagnostic) {
                  diagnostic.result = false;
                  diagnostic.error = String(error?.message || error || "unknown");
                }
              }
            );
          }
          function render() {
            renderSites();
            renderTasks();
          }
          function spaceViewSignature(value) {
            if (!value) return "";
            return JSON.stringify({
              categories: value.categories?.map?.(({ id, title, order }) => ({ id, title, order })) || [],
              sites: value.sites?.map?.(({ id, categoryId, title, url, iconKind, iconValue, iconColor: iconColor2, order }) => ({ id, categoryId, title, url, iconKind, iconValue, iconColor: iconColor2, order })) || [],
              display: value.display || {}
            });
          }
          async function persist(next, successMessage) {
            const previous = space;
            const expectedRevision = Number(space?.revision) || 0;
            space = model.normalizeSpace(next, { language, now: Date.now() });
            renderSites({ animate: true });
            try {
              const response = await send({ type: "NEWTAB_SITE_SPACE_SAVE", space, expectedRevision });
              if (response?.code === "REVISION_CONFLICT" && response.space) {
                const before2 = spaceViewSignature(space);
                space = model.normalizeSpace(response.space, { language, now: Date.now() });
                if (spaceViewSignature(space) !== before2) renderSites({ animate: true });
                toast(copy.conflict);
                return false;
              }
              if (!response?.ok || !response.space) throw new Error(response?.message || copy.failed);
              const before = spaceViewSignature(space);
              space = model.normalizeSpace(response.space, { language, now: Date.now() });
              if (spaceViewSignature(space) !== before) renderSites({ animate: true });
              if (successMessage) toast(successMessage);
              return true;
            } catch (error) {
              space = previous;
              renderSites({ animate: true });
              toast(error.message || copy.failed);
              return false;
            }
          }
          function uid(prefix) {
            return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
          }
          function removeSite(siteId) {
            return persist({ ...space, sites: space.sites.filter((site) => site.id !== siteId) }, copy.deleted);
          }
          function removeCategory(categoryId) {
            if (!windowRef.confirm?.(copy.deleteCategory)) return false;
            return persist({
              ...space,
              categories: space.categories.filter((item) => item.id !== categoryId),
              sites: space.sites.filter((site) => site.categoryId !== categoryId)
            }, copy.deleted);
          }
          function moveSite(siteId, categoryId) {
            const destinationCount = space.sites.filter((site) => site.categoryId === categoryId && site.id !== siteId).length;
            return persist({
              ...space,
              sites: space.sites.map((site) => site.id === siteId ? { ...site, categoryId, order: destinationCount, updatedAt: Date.now() } : site)
            });
          }
          function moveSiteRelative(sourceId, targetId, side = "before") {
            const source = space.sites.find((site) => site.id === sourceId);
            const target = space.sites.find((site) => site.id === targetId);
            if (!source || !target) return false;
            const destination = space.sites.filter((site) => site.categoryId === target.categoryId && site.id !== sourceId).sort((a, b) => a.order - b.order);
            const targetIndex = Math.max(0, destination.findIndex((site) => site.id === targetId));
            const index = Math.min(destination.length, targetIndex + (side === "after" ? 1 : 0));
            destination.splice(index, 0, { ...source, categoryId: target.categoryId, updatedAt: Date.now() });
            const changed = new Map(destination.map((site, order) => [site.id, { ...site, order }]));
            return persist({ ...space, sites: space.sites.map((site) => changed.get(site.id) || site) });
          }
          function moveSiteBefore(sourceId, targetId) {
            return moveSiteRelative(sourceId, targetId, "before");
          }
          function moveCategoryRelative(sourceId, targetId, side = "before") {
            const ordered = [...space.categories].sort((a, b) => a.order - b.order);
            const sourceIndex = ordered.findIndex((item) => item.id === sourceId);
            const targetIndex = ordered.findIndex((item) => item.id === targetId);
            if (sourceIndex < 0 || targetIndex < 0) return false;
            const [source] = ordered.splice(sourceIndex, 1);
            const adjustedTargetIndex = ordered.findIndex((item) => item.id === targetId);
            const insertIndex = Math.min(
              ordered.length,
              Math.max(0, adjustedTargetIndex + (side === "after" ? 1 : 0))
            );
            ordered.splice(insertIndex, 0, source);
            return persist({ ...space, categories: ordered.map((item, order) => ({ ...item, order })) });
          }
          function moveCategory(sourceId, targetId) {
            return moveCategoryRelative(sourceId, targetId, "before");
          }
          function updateDisplayPreference(key, value, persistChange = false) {
            if (!space || !["iconSize", "titleSize"].includes(key)) return false;
            const display = model.normalizeDisplay?.({ ...pendingDisplay || space.display || {}, [key]: value }) || { ...pendingDisplay || space.display || {}, [key]: Number(value) };
            pendingDisplay = display;
            if (!persistChange) {
              const property = key === "iconSize" ? "--site-icon-size" : "--site-title-size";
              documentRef.documentElement?.style?.setProperty?.(property, `${display[key]}px`);
            } else {
              windowRef.clearTimeout?.(displaySaveTimer);
              displaySaveTimer = windowRef.setTimeout?.(() => {
                displaySaveTimer = 0;
                const nextDisplay = pendingDisplay;
                pendingDisplay = null;
                void persist({ ...space, display: nextDisplay });
              }, 120) || 0;
            }
            return true;
          }
          function renderColors() {
            const picker = el("colorPicker");
            picker?.querySelectorAll?.("[data-site-editor-color]").forEach((button) => {
              const selected = button.dataset.siteEditorColor === selectedColor;
              button.classList.toggle("is-selected", selected);
              button.classList.toggle("active", selected);
              button.setAttribute("aria-pressed", String(selected));
            });
          }
          function renderSiteEditorFields(site, categoryId) {
            const container = el("siteEditorFields");
            if (!container) return;
            const selectedCategoryId = site?.categoryId || categoryId || space.categories[0]?.id || "";
            selectedIconSource = site?.iconKind === "letter" ? "text" : "auto";
            container.innerHTML = siteEditorView.buildFieldsMarkup({
              editSite: site,
              allowUpload: false,
              standardAttribute: false,
              copy: editorCopy(),
              categories: [...space.categories].sort((a, b) => a.order - b.order).map((category) => ({ ...category, title: displayCategoryTitle(category) })),
              selectedCategoryId,
              ids: {
                title: "siteTitleInput",
                url: "siteUrlInput",
                category: "siteCategoryInput",
                iconText: "siteIconTextInput",
                iconTextCount: "siteIconTextCount",
                colorPicker: "colorPicker"
              }
            });
            const textInput = el("siteIconTextInput");
            const textField = container.querySelector?.("[data-dock-add-text-icon]");
            const setSource = (source) => {
              selectedIconSource = source === "text" ? "text" : "auto";
              container.querySelectorAll?.("[data-site-editor-icon-source]").forEach((button) => {
                const selected = button.dataset.siteEditorIconSource === selectedIconSource;
                button.setAttribute("aria-pressed", String(selected));
                button.classList.toggle("active", selected);
              });
              if (textField) textField.hidden = selectedIconSource !== "text";
            };
            container.querySelectorAll?.("[data-site-editor-icon-source]").forEach((button) => {
              button.addEventListener("click", () => setSource(button.dataset.siteEditorIconSource));
            });
            container.querySelectorAll?.("[data-site-editor-color]").forEach((button) => {
              button.addEventListener("click", () => {
                selectedColor = button.dataset.siteEditorColor;
                renderColors();
              });
            });
            textInput?.addEventListener?.("input", () => {
              textInput.value = [...textInput.value].slice(0, 2).join("");
              if (el("siteIconTextCount")) el("siteIconTextCount").textContent = `${[...textInput.value].length}/2`;
            });
            selectedColor = site?.iconColor || model.TEXT_ICON_COLORS[0]?.id || "violet";
            setSource(selectedIconSource);
            renderColors();
          }
          function setAddTab(tab) {
            activeAddTab = ["custom", "popular", "bookmarks"].includes(tab) ? tab : "custom";
            el("siteDialogTabs")?.querySelectorAll?.("[data-add-tab]").forEach((button) => {
              button.setAttribute("aria-selected", String(button.dataset.addTab === activeAddTab));
            });
            documentRef.querySelectorAll?.("[data-add-panel]").forEach((panel) => {
              panel.hidden = panel.dataset.addPanel !== activeAddTab;
            });
            if (el("siteSave")) el("siteSave").textContent = editingSiteId ? copy.saveSite : copy.addSite;
            if (activeAddTab === "popular") renderPopular(el("popularSearchInput")?.value || "");
            if (activeAddTab === "bookmarks") void loadBookmarks();
          }
          function pickRecord(site) {
            return {
              id: site.id || "",
              title: String(site.title || "").trim(),
              url: model.safeUrl(site.url),
              iconKind: site.iconKind || (site.iconFile ? "catalog" : "favicon"),
              iconValue: site.iconValue || site.iconFile || "",
              iconColor: site.iconColor || selectedColor
            };
          }
          function pickButton(site) {
            const normalized = pickRecord(site);
            const button = make("button", `pick-site${selectedPickUrls.has(normalized.url) ? " is-selected" : ""}`);
            button.type = "button";
            button.append(siteIcon(normalized));
            const text = make("span", "pick-site-copy");
            text.append(make("strong", "", normalized.title), make("small", "", normalized.url));
            button.append(text);
            button.addEventListener("click", () => {
              if (selectedPickUrls.has(normalized.url)) selectedPickUrls.delete(normalized.url);
              else selectedPickUrls.add(normalized.url);
              button.classList.toggle("is-selected", selectedPickUrls.has(normalized.url));
            });
            return button;
          }
          function renderPopular(query = "") {
            const list = el("popularSiteList");
            if (!list) return;
            const needle = String(query).trim().toLocaleLowerCase();
            const sites = (catalog?.sites || []).filter((site) => !needle || `${site.title} ${site.url} ${site.keywords || ""}`.toLocaleLowerCase().includes(needle)).slice(0, 100);
            list.replaceChildren(...sites.map(pickButton));
          }
          async function loadBookmarks() {
            if (bookmarksLoaded) {
              renderBookmarks();
              return;
            }
            const state = el("bookmarkState");
            const permissionButton = el("bookmarkPermission");
            if (state) {
              state.hidden = false;
              state.textContent = copy.bookmarkLoading;
            }
            if (permissionButton) permissionButton.hidden = true;
            try {
              const response = await send({ type: "DOCK_GET_BOOKMARKS" });
              bookmarks = response?.ok === false ? [] : Array.isArray(response?.bookmarks) ? response.bookmarks : [];
              bookmarksLoaded = response?.ok !== false;
              if (!bookmarksLoaded) {
                const permissionBlocked = /^BOOKMARKS_PERMISSION_(?:REQUIRED|DENIED)$/.test(String(response?.code || ""));
                if (permissionButton) {
                  permissionButton.hidden = !permissionBlocked;
                  permissionButton.disabled = bookmarkPermissionPending;
                }
                if (state) {
                  state.hidden = false;
                  state.textContent = response?.message || (response?.code === "BOOKMARKS_PERMISSION_DENIED" ? copy.permissionDenied : copy.permissionRequired);
                }
                if (permissionBlocked) return;
                throw new Error(response?.message || copy.bookmarkFailed);
              }
              renderBookmarks();
            } catch (error) {
              if (state) {
                state.hidden = false;
                state.textContent = error.message || copy.bookmarkFailed;
              }
            }
          }
          async function requestBookmarkImportPermission() {
            if (bookmarkPermissionPending) return false;
            bookmarkPermissionPending = true;
            const permissionButton = el("bookmarkPermission");
            if (permissionButton) permissionButton.disabled = true;
            const request = requestOptionalPermission("bookmarks", "DOCK_REQUEST_BOOKMARKS_PERMISSION");
            let response;
            try {
              response = await request;
            } catch (_error) {
              response = { ok: false, granted: false, code: "BOOKMARKS_PERMISSION_DENIED" };
            }
            bookmarkPermissionPending = false;
            if (permissionButton) permissionButton.disabled = false;
            if (response?.granted === true) {
              bookmarksLoaded = false;
              await loadBookmarks();
              return true;
            }
            bookmarks = [];
            bookmarksLoaded = false;
            const state = el("bookmarkState");
            if (state) {
              state.hidden = false;
              state.textContent = response?.message || copy.permissionDenied;
            }
            if (permissionButton) permissionButton.hidden = false;
            return false;
          }
          function renderBookmarks() {
            const list = el("bookmarkSiteList");
            const state = el("bookmarkState");
            if (!list) return;
            list.replaceChildren(...bookmarks.filter((item) => model.safeUrl(item.url)).slice(0, 300).map(pickButton));
            if (state) {
              state.hidden = bookmarks.length > 0;
              state.textContent = copy.bookmarkEmpty;
            }
          }
          function openSiteDialog(site = null, categoryId = "") {
            if (!space?.categories?.length) {
              openCategoryDialog();
              return false;
            }
            editingSiteId = site?.id || "";
            siteDialogCategoryId = model.safeId(site?.categoryId || categoryId || space.categories[0]?.id);
            selectedPickUrls.clear();
            renderSiteEditorFields(site, categoryId);
            if (el("siteDialogTitle")) el("siteDialogTitle").textContent = site ? copy.editSite : copy.addSite;
            if (el("siteDialogTabs")) el("siteDialogTabs").hidden = Boolean(site);
            setAddTab("custom");
            const dialog = el("siteDialog");
            if (!dialog) return false;
            dialog.showModal();
            windowRef.setTimeout?.(() => el("siteTitleInput")?.focus?.(), 0);
            return true;
          }
          function showFormError(id, message) {
            const node = el(id);
            if (!node) return;
            node.textContent = message;
            node.hidden = !message;
          }
          function selectedCatalogRecords() {
            const records = [...catalog?.sites || [], ...bookmarks].map(pickRecord);
            const seen = /* @__PURE__ */ new Set();
            return records.filter((record) => {
              if (!selectedPickUrls.has(record.url) || seen.has(record.url)) return false;
              seen.add(record.url);
              return true;
            });
          }
          async function submitSite(event) {
            event.preventDefault();
            if (event.submitter?.value === "cancel") {
              closeDialog(el("siteDialog"), el("addSite"));
              return;
            }
            showFormError("siteFormError", "");
            const now = Date.now();
            if (!editingSiteId && activeAddTab !== "custom") {
              const selected = selectedCatalogRecords();
              if (!selected.length) {
                showFormError("siteFormError", copy.selectOne);
                return;
              }
              const categoryId2 = space.categories.some((item) => item.id === siteDialogCategoryId) ? siteDialogCategoryId : space.categories[0]?.id;
              const existingUrls = new Set(space.sites.map((site) => site.url));
              const additions = selected.filter((site) => !existingUrls.has(site.url)).map((site, offset) => ({
                ...site,
                id: model.safeId(site.id) && !space.sites.some((current2) => current2.id === model.safeId(site.id)) ? model.safeId(site.id) : uid("site"),
                categoryId: categoryId2,
                order: space.sites.filter((item) => item.categoryId === categoryId2).length + offset,
                createdAt: now,
                updatedAt: now
              }));
              if (!additions.length) {
                showFormError("siteFormError", copy.duplicate);
                return;
              }
              if (await persist({ ...space, sites: [...space.sites, ...additions] }, copy.saved)) closeDialog(el("siteDialog"), el("addSite"));
              return;
            }
            const title = model.safeText(el("siteTitleInput")?.value, 80);
            const url = model.safeUrl(el("siteUrlInput")?.value);
            const categoryId = model.safeId(el("siteCategoryInput")?.value);
            if (!title || !url || !categoryId) {
              showFormError("siteFormError", copy.invalidUrl);
              return;
            }
            const duplicate = space.sites.find((site) => site.url === url && site.id !== editingSiteId);
            if (duplicate) {
              showFormError("siteFormError", copy.duplicate);
              return;
            }
            const current = space.sites.find((site) => site.id === editingSiteId);
            const text = model.safeText(el("siteIconTextInput")?.value, 2) || title.slice(0, 1);
            const record = {
              id: current?.id || uid("site"),
              categoryId,
              title,
              url,
              iconKind: selectedIconSource === "text" ? "letter" : catalog?.findByUrl?.(url) ? "catalog" : "favicon",
              iconValue: selectedIconSource === "text" ? text : catalog?.findByUrl?.(url)?.iconFile || "",
              iconColor: selectedColor,
              order: current?.categoryId === categoryId ? current.order : space.sites.filter((site) => site.categoryId === categoryId).length,
              createdAt: current?.createdAt || now,
              updatedAt: now
            };
            const nextSites = current ? space.sites.map((site) => site.id === current.id ? record : site) : [...space.sites, record];
            if (await persist({ ...space, sites: nextSites }, copy.saved)) closeDialog(el("siteDialog"), el("addSite"));
          }
          function openCategoryDialog(category = null) {
            editingCategoryId = category?.id || "";
            if (el("categoryTitleInput")) el("categoryTitleInput").value = category?.title || "";
            if (el("categoryDialogTitle")) el("categoryDialogTitle").textContent = category ? copy.editCategory : copy.addCategory;
            showFormError("categoryFormError", "");
            const dialog = el("categoryDialog");
            if (!dialog) return false;
            dialog.showModal();
            windowRef.setTimeout?.(() => el("categoryTitleInput")?.focus?.(), 0);
            return true;
          }
          async function submitCategory(event) {
            event.preventDefault();
            if (event.submitter?.value === "cancel") {
              closeDialog(el("categoryDialog"), el("addCategory"));
              return;
            }
            const title = model.safeText(el("categoryTitleInput")?.value, 40);
            if (!title) {
              showFormError("categoryFormError", copy.addCategory);
              return;
            }
            const current = space.categories.find((item) => item.id === editingCategoryId);
            const now = Date.now();
            const record = current ? { ...current, title, updatedAt: now } : {
              id: uid("category"),
              title,
              order: space.categories.length,
              createdAt: now,
              updatedAt: now
            };
            const categories = current ? space.categories.map((item) => item.id === current.id ? record : item) : [...space.categories, record];
            if (await persist({ ...space, categories }, copy.saved)) closeDialog(el("categoryDialog"), el("addCategory"));
          }
          function localSearch(query) {
            return searchContract.searchLocalSites({
              query,
              sites: space?.sites || [],
              catalogSites: catalog?.sites || [],
              pinnedUrls: dockUrls,
              limit: 7
            });
          }
          function escapeHtml(value) {
            return String(value == null ? "" : value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
          }
          function searchTranslate(key) {
            return i18n?.createTranslator?.(language)?.(key, key) || key;
          }
          async function sendSearch(message) {
            if (message?.type === "DOCK_REQUEST_BOOKMARKS_PERMISSION") {
              return requestOptionalPermission("bookmarks", message.type);
            }
            if (message?.type === "DOCK_REQUEST_HISTORY_PERMISSION") {
              return requestOptionalPermission("history", message.type);
            }
            const response = await send(message);
            if (message?.type !== "DOCK_SEARCH_SUGGEST" || response?.ok !== true) return response;
            return {
              ...response,
              suggestions: [...searchContract.composeSuggestions(
                localSearch(message.query),
                response.suggestions
              )]
            };
          }
          function syncSearchDialogHost() {
            if (!searchDialogHost || !searchDialogRuntime) return;
            const open = searchDialogRuntime.isOpen?.() === true;
            searchDialogHost.hidden = !open;
            if (!open) {
              suppressQuickSearchFocus = true;
              windowRef.setTimeout?.(() => {
                suppressQuickSearchFocus = false;
              }, 0);
            }
          }
          function ensureSearchDialog() {
            if (searchDialogRuntime) return true;
            if (typeof options.createSearchDialogRuntime === "function") {
              searchDialogRuntime = options.createSearchDialogRuntime({
                send: sendSearch,
                contract: searchContract,
                getQuery: () => el("quickSearch")?.value || ""
              });
              return Boolean(searchDialogRuntime);
            }
            const parent = documentRef.body || documentRef.documentElement;
            const host = documentRef.createElement?.("div");
            if (!host?.attachShadow || !parent?.appendChild) return false;
            host.id = "newtabDockSearchHost";
            host.className = "newtab-dock-search-host";
            host.dataset.theme = documentRef.documentElement.dataset.theme || "light";
            host.hidden = true;
            const shadow = host.attachShadow({ mode: "open" });
            const stylesheet = documentRef.createElement("link");
            stylesheet.rel = "stylesheet";
            stylesheet.href = chromeApi.runtime.getURL("styles.css");
            shadow.appendChild(stylesheet);
            parent.appendChild(host);
            searchDialogHost = host;
            searchDialogRuntime = searchDialogModule.createDockSearchDialog({
              chrome: chromeApi,
              document: documentRef,
              window: windowRef,
              send: sendSearch,
              escapeHtml,
              context: () => ({ root: shadow, searchButton: el("quickSearch") }),
              closeOthers: () => {
                for (const id of ["siteDialog", "categoryDialog"]) {
                  const dialog = el(id);
                  if (dialog?.open) closeDialog(dialog);
                }
              },
              syncHost: syncSearchDialogHost,
              showToast: (message) => toast(message),
              setExpanded: () => {
              },
              translate: searchTranslate,
              setTimeoutFn: windowRef.setTimeout?.bind(windowRef) || setTimeout,
              clearTimeoutFn: windowRef.clearTimeout?.bind(windowRef) || clearTimeout,
              requestFrame: windowRef.requestAnimationFrame?.bind(windowRef) || ((callback) => windowRef.setTimeout?.(callback, 0)),
              cancelFrame: windowRef.cancelAnimationFrame?.bind(windowRef) || ((id) => windowRef.clearTimeout?.(id)),
              debounceMs: SEARCH_DEBOUNCE_MS
            });
            return true;
          }
          function openSearch(query = "") {
            if (!ensureSearchDialog()) return false;
            return searchDialogRuntime.open(el("quickSearch"), query);
          }
          async function requestOptionalPermission(permission, messageType) {
            if (chromeApi.permissions?.request) {
              let result;
              try {
                const granted = await chromeApi.permissions.request({ permissions: [permission] }) === true;
                result = {
                  ok: granted,
                  granted,
                  permission,
                  code: granted ? "" : `${permission.toUpperCase()}_PERMISSION_DENIED`,
                  recoverable: true
                };
              } catch (_error) {
                result = {
                  ok: false,
                  granted: false,
                  permission,
                  code: `${permission.toUpperCase()}_PERMISSION_DENIED`,
                  recoverable: true
                };
              }
              let syncResponse = null;
              try {
                syncResponse = await send({
                  type: "DOCK_SEARCH_SYNC_PERMISSIONS",
                  permission: result.permission,
                  granted: result.granted,
                  code: result.code,
                  recoverable: result.recoverable
                });
              } catch (_error) {
              }
              const authoritativePermission = syncResponse?.ok === true ? syncResponse?.permissions?.[permission] || syncResponse : null;
              return {
                ...result,
                ...authoritativePermission || {},
                authoritySynced: syncResponse?.ok === true && syncResponse?.synced === true,
                authorityRevision: Number(syncResponse?.revision) || 0,
                authorityToken: String(syncResponse?.token || ""),
                authorityPermissions: syncResponse?.ok === true ? syncResponse?.permissions || null : null
              };
            }
            return await send({ type: messageType });
          }
          function bindDialogDismiss(dialog, restoreFocus) {
            if (!dialog) return;
            const dismiss = () => closeDialog(dialog, typeof restoreFocus === "function" ? restoreFocus() : restoreFocus);
            dialog.querySelector?.("[data-dialog-close]")?.addEventListener("click", (event) => {
              event.preventDefault();
              dismiss();
            });
            dialog.addEventListener("click", (event) => {
              if (event.target === dialog) {
                event.preventDefault();
                dismiss();
              }
            });
            dialog.addEventListener("cancel", (event) => {
              event.preventDefault();
              dismiss();
            });
          }
          function renderTheme() {
            const dark = themePreference === "dark" || themePreference === "system" && colorSchemeQuery?.matches === true;
            const renderedTheme = dark ? "dark" : "light";
            documentRef.documentElement.dataset.theme = renderedTheme;
            if (searchDialogHost) searchDialogHost.dataset.theme = renderedTheme;
          }
          function persistTheme(value, expectedRevision = null) {
            const queuedRevision = themePreferenceRevision;
            themeLastQueuedRevision = Math.max(themeLastQueuedRevision, queuedRevision);
            const operation = themeWriteQueue.then(async () => {
              if (destroyed || expectedRevision != null && expectedRevision !== themePreferenceRevision) return false;
              let wrote = false;
              try {
                await chromeApi.storage?.sync?.set?.({ [THEME_KEY]: value });
                wrote = true;
              } catch (_error) {
                return false;
              }
              const current = !destroyed && (expectedRevision == null || expectedRevision === themePreferenceRevision);
              if (!current && wrote && !destroyed && themeLastQueuedRevision < themePreferenceRevision) {
                void persistTheme(themePreference, themePreferenceRevision);
              }
              return current;
            });
            themeWriteQueue = operation.catch(() => false);
            return operation;
          }
          function applyTheme(theme, { migrate = true, revision = themePreferenceRevision } = {}) {
            const normalized = normalizeThemePreference(theme);
            themePreference = normalized.value;
            renderTheme();
            if (migrate && normalized.migrated) {
              void persistTheme(normalized.value, revision);
            }
          }
          async function toggleTheme() {
            if (destroyed) return false;
            themePreferenceRevision += 1;
            const next = documentRef.documentElement.dataset.theme === "dark" ? "light" : "dark";
            applyTheme(next, { migrate: false, revision: themePreferenceRevision });
            await persistTheme(next, themePreferenceRevision);
            return true;
          }
          const onSystemThemeChanged = () => {
            if (!destroyed && themePreference === "system") renderTheme();
          };
          function applyTaskSnapshot(snapshot) {
            tasks = snapshot.tasks;
            openTabs = snapshot.openTabs;
            renderTasks({ force: true });
            return snapshot.ok;
          }
          function commitOrDeferTaskSnapshot(snapshot) {
            if (taskRenderBlocked()) {
              deferredTaskSnapshot = snapshot;
              taskRenderPending = true;
              return snapshot.ok;
            }
            deferredTaskSnapshot = null;
            return applyTaskSnapshot(snapshot);
          }
          function flushDeferredTaskRefresh() {
            if (destroyed || taskRenderBlocked()) return false;
            if (taskRefreshPending) {
              deferredTaskSnapshot = null;
              taskRenderPending = false;
              void scheduleTaskRefresh();
              return true;
            }
            if (deferredTaskSnapshot) {
              const snapshot = deferredTaskSnapshot;
              deferredTaskSnapshot = null;
              applyTaskSnapshot(snapshot);
              return true;
            }
            if (taskRenderPending) {
              renderTasks({ force: true });
              return true;
            }
            return false;
          }
          async function loadTasks() {
            const sequence = ++taskLoadSequence;
            try {
              const [taskResponse, tabsResponse] = await Promise.all([
                send({ type: "V2_TASK_LIST" }),
                send({ type: "GET_ALL_TABS" })
              ]);
              if (!taskResponse?.ok) throw new Error(taskResponse?.message || copy.failed);
              if (sequence !== taskLoadSequence) return false;
              return commitOrDeferTaskSnapshot({
                ok: true,
                tasks: projectTaskData(taskResponse),
                openTabs: Array.isArray(tabsResponse?.tabs) ? tabsResponse.tabs : []
              });
            } catch (_error) {
              if (sequence !== taskLoadSequence) return false;
              return commitOrDeferTaskSnapshot({ ok: false, tasks: [], openTabs: [] });
            }
          }
          function scheduleTaskRefresh(epoch = 0) {
            pendingTaskRefreshEpoch = Math.max(pendingTaskRefreshEpoch, Number(epoch) || 0);
            taskRefreshPending = true;
            if (taskRenderBlocked()) return taskRefreshWork || Promise.resolve(false);
            if (taskRefreshWork) return taskRefreshWork;
            taskRefreshWork = (async () => {
              await Promise.resolve();
              while (taskRefreshPending) {
                if (taskRenderBlocked()) break;
                taskRefreshPending = false;
                const refreshEpoch = pendingTaskRefreshEpoch;
                pendingTaskRefreshEpoch = 0;
                await loadTasks();
                lastTaskChangeEpoch = Math.max(lastTaskChangeEpoch, refreshEpoch);
              }
            })().finally(() => {
              taskRefreshWork = null;
              if (taskRefreshPending && !taskRenderBlocked() && !destroyed) void scheduleTaskRefresh();
            });
            return taskRefreshWork;
          }
          function onTaskTabChanged() {
            if (!destroyed) void scheduleTaskRefresh();
          }
          async function openTask(task) {
            const openUrls = new Set(openTabs.map((tab) => model.safeUrl(tab.url)).filter(Boolean));
            for (const page of task.pages || []) {
              const url = model.safeUrl(page.url);
              if (!url || openUrls.has(url)) continue;
              openUrls.add(url);
              const response = await send({ type: "NEW_TAB", url, active: false });
              if (!response?.ok) {
                toast(response?.message || copy.failed);
                return false;
              }
            }
            await loadTasks();
            toast(copy.taskOpened);
            return true;
          }
          async function closeTask(task) {
            const urls = new Set((task.pages || []).map((page) => model.safeUrl(page.url)).filter(Boolean));
            const tabIds = openTabs.filter((tab) => urls.has(model.safeUrl(tab.url))).map((tab) => Number(tab.id)).filter(Number.isInteger);
            if (!tabIds.length) {
              toast(copy.taskNoOpen);
              return false;
            }
            const response = await send({ type: "CLOSE_TABS", tabIds });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              return false;
            }
            await loadTasks();
            toast(copy.taskClosed);
            return true;
          }
          async function updateTaskField(task, patch, successMessage = copy.taskUpdated) {
            const response = await send({ type: "V2_TASK_UPDATE", taskId: task.id, patch });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              return false;
            }
            await loadTasks();
            toast(successMessage);
            return true;
          }
          async function renameTask(task) {
            const title = windowRef.prompt?.(copy.taskRenamePrompt, task.title || "");
            if (title == null) return false;
            const normalized = model.safeText(title, 120);
            if (!normalized || normalized === task.title) return false;
            return updateTaskField(task, { title: normalized }, copy.taskRenamed);
          }
          async function dissolveTask(task) {
            if (windowRef.confirm?.(copy.taskDissolveConfirm) !== true) return false;
            const replacementTaskIdsByPage = {};
            for (const page of task.pages || []) {
              if (page?.relation?.isPrimary !== true) continue;
              const candidates = replacementTaskCandidates(tasks, task.id, page.id);
              const replacementTaskId = await resolveReplacementTaskId(candidates, (choices) => chooseTaskReplacement({
                task,
                page,
                candidates: choices.map((candidate) => ({ id: candidate.id, title: candidate.title }))
              }));
              if (replacementTaskId == null) {
                toast(copy.taskReplacementCancelled);
                return false;
              }
              if (replacementTaskId) replacementTaskIdsByPage[page.id] = replacementTaskId;
            }
            const response = await send({
              type: "V2_TASK_DELETE",
              taskId: task.id,
              replacementTaskIdsByPage
            });
            if (!response?.ok) {
              toast(response?.message || copy.failed);
              await loadTasks();
              return false;
            }
            await loadTasks();
            toast(copy.taskDissolved);
            return true;
          }
          function applyLanguagePreference(manual) {
            const next = i18n?.resolveLanguage ? i18n.resolveLanguage({ manual, browserLanguage, navigatorLanguage: windowRef.navigator?.language }) : manual === "zh" || manual === "zh-CN" ? "zh" : manual === "en" ? "en" : String(browserLanguage).toLowerCase().startsWith("zh") ? "zh" : "en";
            if (next === language) {
              applyCopy();
              return false;
            }
            const dialogOpen = el("siteDialog")?.open === true;
            const draft = dialogOpen ? {
              ...currentSite(editingSiteId) || {},
              title: el("siteTitleInput")?.value || "",
              url: el("siteUrlInput")?.value || "",
              categoryId: el("siteCategoryInput")?.value || siteDialogCategoryId,
              iconKind: selectedIconSource === "text" ? "letter" : "favicon",
              iconValue: el("siteIconTextInput")?.value || "",
              iconColor: selectedColor
            } : null;
            language = next;
            copy = COPY[language];
            applyCopy();
            renderSites();
            renderTasks();
            if (dialogOpen) renderSiteEditorFields(draft, draft.categoryId);
            if (activeAddTab === "popular") renderPopular(el("popularSearchInput")?.value || "");
            if (activeAddTab === "bookmarks" && bookmarksLoaded) renderBookmarks();
            return true;
          }
          function onStorageChanged(changes, areaName) {
            if (areaName === "local" && changes?.[model.STORAGE_KEY]?.newValue) {
              const incoming = model.normalizeSpace(changes[model.STORAGE_KEY].newValue, { language, now: Date.now() });
              if (!space || Number(incoming.revision) >= Number(space.revision)) {
                const changed = spaceViewSignature(incoming) !== spaceViewSignature(space);
                space = incoming;
                if (changed) renderSites({ animate: true });
              }
            }
            if (areaName === "sync" && changes?.[THEME_KEY]) {
              themePreferenceRevision += 1;
              applyTheme(changes[THEME_KEY].newValue, { revision: themePreferenceRevision });
            }
            if (areaName === "sync" && changes?.[LANGUAGE_KEY]) applyLanguagePreference(changes[LANGUAGE_KEY].newValue);
            if (areaName === "sync" && changes?.[DOCK_KEY]) {
              dockUrls = new Set((changes[DOCK_KEY].newValue?.sites || []).map((site) => model.safeUrl(site.url)).filter(Boolean));
              renderSites();
            }
          }
          function onMessage(message, _sender, sendResponse) {
            if (message?.type === "V2_TASK_DATA_CHANGED" && message.surface === "newtab") {
              const token = String(message.token || "");
              const epoch = Number(message.epoch ?? message.revision) || 0;
              if (token && token === lastTaskChangeToken || epoch && epoch <= lastTaskChangeEpoch) {
                sendResponse({ ok: true, duplicate: true, epoch: lastTaskChangeEpoch });
                return false;
              }
              if (token) lastTaskChangeToken = token;
              void scheduleTaskRefresh(epoch);
              sendResponse({ ok: true, refreshed: true, epoch });
              return false;
            }
            if (message?.type === "WEB_DOCK_SEARCH_PERMISSIONS_CHANGED") {
              const token = String(message.token || "");
              const revision = Number(message.revision) || 0;
              const staleRevision = revision > 0 && revision <= lastSearchPermissionBroadcastRevision;
              if (staleRevision || token && token === lastSearchPermissionBroadcastToken) {
                sendResponse({
                  ok: true,
                  duplicate: true,
                  revision: lastSearchPermissionBroadcastRevision
                });
                return false;
              }
              if (token) lastSearchPermissionBroadcastToken = token;
              if (revision > 0) lastSearchPermissionBroadcastRevision = revision;
              const shouldRefresh = searchDialogRuntime?.isOpen?.() === true;
              searchDialogRuntime?.handlePermissionsChanged?.(message);
              sendResponse({
                ok: true,
                refreshed: shouldRefresh,
                revision: lastSearchPermissionBroadcastRevision
              });
              return false;
            }
            if (!isTargetMessage(message, currentTabId)) return false;
            openWorkspace().then((ok) => sendResponse({ ok, tabId: currentTabId }));
            return true;
          }
          function bind() {
            if (bound) return;
            bound = true;
            chromeApi.runtime.onMessage.addListener(onMessage);
            chromeApi.storage?.onChanged?.addListener?.(onStorageChanged);
            chromeApi.tabs?.onCreated?.addListener?.(onTaskTabChanged);
            chromeApi.tabs?.onUpdated?.addListener?.(onTaskTabChanged);
            chromeApi.tabs?.onRemoved?.addListener?.(onTaskTabChanged);
            documentRef.addEventListener?.("dragover", onTaskDragOverCapture, true);
            documentRef.addEventListener?.("drop", onTaskDropCapture, true);
            if (colorSchemeQuery?.addEventListener) colorSchemeQuery.addEventListener("change", onSystemThemeChanged);
            else colorSchemeQuery?.addListener?.(onSystemThemeChanged);
            el("openWorkspace")?.addEventListener("click", openWorkspace);
            el("taskEmptyOpen")?.addEventListener("click", openWorkspace);
            el("settingsButton")?.addEventListener("click", openSettings);
            el("themeToggle")?.addEventListener("click", toggleTheme);
            el("addSite")?.addEventListener("click", () => openSiteDialog());
            el("addCategory")?.addEventListener("click", () => openCategoryDialog());
            el("emptyAddCategory")?.addEventListener("click", () => openCategoryDialog());
            el("refreshTasks")?.addEventListener("click", loadTasks);
            el("siteForm")?.addEventListener("submit", submitSite);
            el("categoryForm")?.addEventListener("submit", submitCategory);
            bindDialogDismiss(el("siteDialog"), () => el("addSite"));
            bindDialogDismiss(el("categoryDialog"), () => el("addCategory"));
            ensureSearchDialog();
            el("siteDialogTabs")?.addEventListener("click", (event) => {
              const button = event.target.closest?.("[data-add-tab]");
              if (button) setAddTab(button.dataset.addTab);
            });
            el("bookmarkPermission")?.addEventListener("click", () => {
              void requestBookmarkImportPermission();
            });
            el("popularSearchInput")?.addEventListener("input", (event) => renderPopular(event.target.value));
            el("quickSearchForm")?.addEventListener("submit", (event) => {
              event.preventDefault();
              openSearch(el("quickSearch")?.value || "");
            });
            el("quickSearch")?.addEventListener("focus", () => {
              if (!suppressQuickSearchFocus) openSearch(el("quickSearch")?.value || "");
            });
            el("quickSearch")?.addEventListener("click", () => {
              if (!suppressQuickSearchFocus && searchDialogRuntime?.isOpen?.() !== true) openSearch(el("quickSearch")?.value || "");
            });
            el("iconSizeRange")?.addEventListener("input", (event) => updateDisplayPreference("iconSize", event.target.value));
            el("iconSizeRange")?.addEventListener("change", (event) => updateDisplayPreference("iconSize", event.target.value, true));
            el("titleSizeRange")?.addEventListener("input", (event) => updateDisplayPreference("titleSize", event.target.value));
            el("titleSizeRange")?.addEventListener("change", (event) => updateDisplayPreference("titleSize", event.target.value, true));
            retryButton?.addEventListener("click", start);
          }
          async function load() {
            const themeRevisionAtRead = themePreferenceRevision;
            const [spaceResponse, dockResponse, storedSettings] = await Promise.all([
              send({ type: "NEWTAB_SITE_SPACE_GET" }),
              send({ type: "DOCK_GET_CONFIG" }).catch(() => ({ ok: false })),
              readSync([THEME_KEY, LANGUAGE_KEY])
            ]);
            if (!spaceResponse?.ok || !spaceResponse.space) throw new Error(spaceResponse?.message || copy.failed);
            applyLanguagePreference(storedSettings?.[LANGUAGE_KEY]);
            space = model.normalizeSpace(spaceResponse.space, { language, now: Date.now() });
            dockUrls = new Set((dockResponse?.config?.sites || []).map((site) => model.safeUrl(site.url)).filter(Boolean));
            if (themeRevisionAtRead === themePreferenceRevision) {
              applyTheme(storedSettings?.[THEME_KEY], { revision: themeRevisionAtRead });
            }
            await loadTasks();
            return true;
          }
          async function start() {
            if (destroyed) return false;
            try {
              applyTheme(themePreference, { migrate: false });
              applyCopy();
              bind();
              currentTabId = Number((await chromeApi.tabs?.getCurrent?.())?.id);
              if (!Number.isInteger(currentTabId)) currentTabId = null;
              documentRef.title = chromeApi.i18n?.getMessage?.("newTabTitle") || "TabFlow";
              await load();
              render();
              setFailure(false);
              if (!observer && typeof windowRef.MutationObserver === "function") {
                observer = new windowRef.MutationObserver(markWorkspaceSurface);
              }
              observer?.observe?.(documentRef.documentElement, { childList: true, subtree: true });
              markWorkspaceSurface();
              return true;
            } catch (error) {
              setFailure(true, `${copy.failed}${error?.message ? `\uFF1A${error.message}` : ""}`);
              return false;
            }
          }
          function destroy() {
            if (destroyed) return false;
            destroyed = true;
            observer?.disconnect?.();
            observer = null;
            chromeApi.runtime.onMessage.removeListener(onMessage);
            chromeApi.storage?.onChanged?.removeListener?.(onStorageChanged);
            chromeApi.tabs?.onCreated?.removeListener?.(onTaskTabChanged);
            chromeApi.tabs?.onUpdated?.removeListener?.(onTaskTabChanged);
            chromeApi.tabs?.onRemoved?.removeListener?.(onTaskTabChanged);
            documentRef.removeEventListener?.("dragover", onTaskDragOverCapture, true);
            documentRef.removeEventListener?.("drop", onTaskDropCapture, true);
            if (colorSchemeQuery?.removeEventListener) colorSchemeQuery.removeEventListener("change", onSystemThemeChanged);
            else colorSchemeQuery?.removeListener?.(onSystemThemeChanged);
            taskLoadSequence += 1;
            dragRenderGuard = false;
            deferredTaskSnapshot = null;
            taskRenderPending = false;
            windowRef.clearTimeout?.(dragRefreshFlushTimer);
            dragRefreshFlushTimer = 0;
            windowRef.clearTimeout?.(toastTimer);
            windowRef.clearTimeout?.(displaySaveTimer);
            searchDialogRuntime?.destroy?.();
            searchDialogRuntime = null;
            searchDialogHost?.remove?.();
            searchDialogHost = null;
            return true;
          }
          return Object.freeze({
            start,
            destroy,
            openWorkspace,
            openSettings,
            markWorkspaceSurface,
            render,
            loadTasks,
            inspect: () => ({
              destroyed,
              currentTabId,
              space,
              tasks: tasks.map((task) => ({ ...task })),
              lastTaskChangeEpoch,
              lastTaskChangeToken,
              dragRenderGuard,
              taskRenderPending,
              taskRefreshPending,
              deferredTaskSnapshot: Boolean(deferredTaskSnapshot),
              lastTaskDragDiagnostic: lastTaskDragDiagnostic ? JSON.parse(JSON.stringify(lastTaskDragDiagnostic)) : null,
              searchPermissions: { ...searchDialogRuntime?.inspect?.().permissions || {} },
              searchSuggestions: (searchDialogRuntime?.inspect?.().suggestions || []).map((item) => ({ ...item })),
              lastSearchPermissionBroadcastRevision,
              themePreference,
              themePreferenceRevision
            })
          });
        }
        return Object.freeze({
          TOGGLE_MESSAGE,
          WORKSPACE_HOST_SELECTOR,
          SEARCH_DEBOUNCE_MS,
          isTargetMessage,
          projectTaskData,
          normalizeThemePreference,
          reorderIds,
          insertIdRelative,
          decodeDragPayload,
          replacementTaskCandidates,
          resolveReplacementTaskId,
          createNewTabRuntime
        });
      });
    }
  });

  // src/newtab/index.js
  var require_index = __commonJS({
    "src/newtab/index.js"() {
      var import_dock_logic = __toESM(require_dock_logic());
      var import_dock_search_contract = __toESM(require_dock_search_contract());
      var import_i18n = __toESM(require_i18n());
      var import_web_dock_catalog = __toESM(require_web_dock_catalog());
      var import_site_editor_view = __toESM(require_site_editor_view());
      var import_newtab_site_space_model = __toESM(require_newtab_site_space_model());
      var import_dock_search_dialog = __toESM(require_dock_search_dialog());
      var import_runtime = __toESM(require_runtime());
      var bootstrap = globalThis.__TFA_OWNER__?.bootstrap;
      if (globalThis.__TFA_OWNER__) globalThis.__TFA_OWNER__.surfaceMode = "newtab-site-space";
      var runtime = globalThis.TfaNewTabRuntime.createNewTabRuntime({
        window,
        document,
        chrome,
        bootstrap,
        model: globalThis.TfaNewTabSiteSpaceModel,
        catalog: globalThis.TfaWebDockCatalog,
        i18n: globalThis.TfaI18n,
        searchContract: globalThis.TfaDockSearchContract,
        searchDialog: globalThis.TfaDockSearchDialog
      });
      globalThis.__TFA_NEWTAB_RUNTIME__ = runtime;
      void runtime.start();
    }
  });
  require_index();
})();
