<script setup>
import { computed, inject, onBeforeUnmount, provide, ref, watch } from "vue";
import { Icon } from "@iconify/vue";
import {
  ActionSchemaFields,
  ActionsConfig,
  getStructFieldDescription,
  getStructFieldTitle
} from "./schema";
import { Environment } from "../../vendor/cel-js/lib/index";
import { provideCELContext } from "../../utils/cel-context";
import { BUTTONS, findButton, ITEMS, findItem } from "./utils/resolve";
import { SIDEBAR_TAKEOVER_KEY } from "../sidebar-takeover";
import { BREADCRUMB_EXTENSION_KEY } from "../config/breadcrumb-extension";
import { ScrollArea } from "../ui/scroll-area";
import { Field, FieldLabel } from "../ui/field";
import { Textarea } from "../ui/textarea";
import { ExpressionEditor } from "../ui/expression-editor";
import { Switch } from "../ui/switch";
import { Separator } from "../ui/separator";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import { Locale as LocaleField } from "../ui/locale";
import { IconPicker } from "../ui/icon-picker";
import {
  InputGroup,
  InputGroupAddon,
  InputGroupCombobox,
  InputGroupNumberField
} from "../ui/input-group";
import { CommandGroup, CommandItem } from "../ui/command";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue
} from "../ui/select";
import { getLocalizedText } from "../../share/locale";
import { Markdown } from "../ui/markdown";
import {
  useTreeDnd
} from "../../composables/useTreeDnd";
import {
  provideEventAncestor
} from "../../share/event-bus";
defineOptions({ name: "ShwfedActionsConfig" });
const props = defineProps({
  configure: { type: Function, required: false }
});
const config = defineModel("modelValue", { type: null, ...{ required: true } });
const configure = props.configure ?? (() => {
});
const actionsSchema = ActionsConfig(configure);
const advertisedContext = (() => {
  const probe = new Environment({ unlistedVariablesAreDyn: false });
  const baseline = new Set(probe.getDefinitions().variables.map((v) => v.name));
  configure(probe);
  const out = {};
  for (const v of probe.getDefinitions().variables) {
    if (baseline.has(v.name)) continue;
    out[v.name] = {
      type: v.type,
      label: v.label ?? v.name,
      description: v.description ?? void 0,
      value: void 0
    };
  }
  return out;
})();
provideCELContext(advertisedContext);
const {
  ActionGroup: ActionGroupSchema,
  ActionItem: ActionItemSchema,
  DropdownAction: DropdownActionSchema,
  DropdownActionItem: DropdownActionItemSchema
} = ActionSchemaFields(configure);
const groupFieldTitle = (f) => getStructFieldTitle(ActionGroupSchema, f) ?? f;
const itemFieldTitle = (f) => getStructFieldTitle(ActionItemSchema, f) ?? f;
const dropdownItemFieldTitle = (f) => getStructFieldTitle(DropdownActionItemSchema, f) ?? f;
const subFieldTitle = (f) => getStructFieldTitle(DropdownActionSchema, f) ?? f;
const generalFieldTitle = (f) => getStructFieldTitle(actionsSchema, f) ?? f;
const groupFieldDescription = (f) => getStructFieldDescription(ActionGroupSchema, f);
const itemFieldDescription = (f) => getStructFieldDescription(ActionItemSchema, f);
const dropdownItemFieldDescription = (f) => getStructFieldDescription(DropdownActionItemSchema, f);
const subFieldDescription = (f) => getStructFieldDescription(DropdownActionSchema, f);
const generalFieldDescription = (f) => getStructFieldDescription(actionsSchema, f);
function isDropdownItem(item) {
  return "items" in item;
}
function isRegistryItem(item) {
  return "type" in item && "compatibilityDate" in item;
}
function isRegistrySub(child) {
  return "type" in child && "compatibilityDate" in child;
}
const GROUP_DRAG_KIND = "group";
const ITEM_DRAG_KIND = "item";
const SUBITEM_DRAG_KIND = "sub";
const VARIANT_OPTIONS = [
  { value: "default", label: "\u9ED8\u8BA4" },
  { value: "primary", label: "\u4E3B\u8981" },
  { value: "destructive", label: "\u5371\u9669" },
  { value: "ghost", label: "\u900F\u660E" },
  { value: "link", label: "\u94FE\u63A5" }
];
const SIZE_OPTIONS = [
  { value: "default", label: "\u9ED8\u8BA4" },
  { value: "sm", label: "\u5C0F" },
  { value: "xs", label: "\u6781\u5C0F" }
];
const ITEM_PICKER_ENTRIES = ITEMS.map((i) => ({
  type: i.type,
  compatibilityDate: i.compatibilityDate,
  name: i.metadata.name,
  icon: i.metadata.icon
})).sort((a, b) => a.name.localeCompare(b.name, "zh"));
const ACTION_PICKER_ENTRIES = (() => {
  const byType = /* @__PURE__ */ new Map();
  for (const a of BUTTONS) {
    if (a.deprecated) continue;
    const prev = byType.get(a.type);
    if (!prev || a.compatibilityDate > prev.compatibilityDate) {
      byType.set(a.type, {
        type: a.type,
        compatibilityDate: a.compatibilityDate,
        name: a.metadata.name,
        icon: a.metadata.icon
      });
    }
  }
  return [...byType.values()].sort((a, b) => a.name.localeCompare(b.name, "zh"));
})();
const editingGroups = computed(
  () => config.value.groups ?? []
);
const editingItems = computed(
  () => config.value.items ?? []
);
function snapshot() {
  return {
    groups: [...config.value.groups ?? []],
    items: [...config.value.items ?? []]
  };
}
function commit(w) {
  config.value = { ...config.value, groups: w.groups, items: w.items };
}
const editingSize = computed({
  get: () => config.value.size ?? "default",
  set: (next) => {
    config.value = { ...config.value, size: next };
  }
});
const editingGap = computed({
  get: () => config.value.gap ?? 4,
  set: (next) => {
    config.value = { ...config.value, gap: next };
  }
});
const editingStyle = computed({
  get: () => config.value.style ?? "",
  set: (next) => {
    const cleaned = next;
    if (cleaned.trim() === "") {
      const { style: _drop, ...rest } = config.value;
      config.value = rest;
    } else {
      config.value = { ...config.value, style: cleaned };
    }
  }
});
const selected = ref({ kind: "general" });
function itemsOfGroup(groupId) {
  return editingItems.value.filter((it) => it.groupId === groupId);
}
function itemLabel(item) {
  if (isRegistryItem(item)) {
    const entry = findItem(item.type, item.compatibilityDate);
    const candidate = item.name;
    if (typeof candidate === "string" && candidate.trim().length > 0) return candidate;
    return entry ? `\u672A\u547D\u540D${entry.metadata.name}` : "\u672A\u547D\u540D\u9879";
  }
  const fallback = isDropdownItem(item) ? "\u672A\u547D\u540D\u4E0B\u62C9\u6309\u94AE" : "\u672A\u547D\u540D\u6309\u94AE";
  return getLocalizedText(item.title, "zh") ?? fallback;
}
function itemIcon(item) {
  if (isRegistryItem(item)) {
    return findItem(item.type, item.compatibilityDate)?.metadata.icon;
  }
  return item.icon;
}
function subLabel(sub) {
  if (isRegistrySub(sub)) {
    const entry = findItem(sub.type, sub.compatibilityDate);
    const candidate = sub.name;
    if (typeof candidate === "string" && candidate.trim().length > 0) return candidate;
    return entry ? `\u672A\u547D\u540D${entry.metadata.name}` : "\u672A\u547D\u540D\u9879";
  }
  return getLocalizedText(sub.title, "zh") ?? "\u672A\u547D\u540D\u6309\u94AE";
}
function subIcon(sub) {
  if (isRegistrySub(sub)) {
    return findItem(sub.type, sub.compatibilityDate)?.metadata.icon;
  }
  return sub.icon;
}
function composeGroupLabel(groupId) {
  const items = itemsOfGroup(groupId);
  if (items.length === 0) return "";
  const first = itemLabel(items[0]);
  if (items.length === 1) return first;
  if (items.length === 2) return `${first}\u3001${itemLabel(items[1])}`;
  return `${first} \u7B49 ${items.length} \u4E2A\u6309\u94AE`;
}
function findItemNode(id) {
  return editingItems.value.find((it) => it.id === id) ?? null;
}
function findSub(id) {
  for (const it of editingItems.value) {
    if (!isDropdownItem(it)) continue;
    const sub = it.items.find((s) => s.id === id);
    if (sub) return { parent: it, sub };
  }
  return null;
}
function findGroup(id) {
  return editingGroups.value.find((g) => g.id === id) ?? null;
}
const selectedGeneral = computed(() => selected.value?.kind === "general");
const selectedGroup = computed(() => {
  if (selected.value?.kind !== "group") return null;
  return findGroup(selected.value.id);
});
const selectedActionItem = computed(() => {
  if (selected.value?.kind !== "item") return null;
  const it = findItemNode(selected.value.id);
  if (!it || isDropdownItem(it) || isRegistryItem(it)) return null;
  return it;
});
const selectedDropdownItem = computed(() => {
  if (selected.value?.kind !== "item") return null;
  const it = findItemNode(selected.value.id);
  if (!it || !isDropdownItem(it)) return null;
  return it;
});
const selectedRegistryItem = computed(() => {
  if (selected.value?.kind !== "item") return null;
  const it = findItemNode(selected.value.id);
  if (!it || !isRegistryItem(it)) return null;
  return it;
});
const selectedRegistryEntry = computed(() => {
  const it = selectedRegistryItem.value;
  if (!it) return null;
  return findItem(it.type, it.compatibilityDate) ?? null;
});
const selectedSubRef = computed(() => {
  if (selected.value?.kind !== "sub") return null;
  const found = findSub(selected.value.id);
  if (!found || isRegistrySub(found.sub)) return null;
  return found.sub;
});
const selectedRegistrySub = computed(() => {
  if (selected.value?.kind !== "sub") return null;
  const found = findSub(selected.value.id);
  if (!found || !isRegistrySub(found.sub)) return null;
  return found.sub;
});
const selectedRegistrySubEntry = computed(() => {
  const sub = selectedRegistrySub.value;
  if (!sub) return null;
  return findItem(sub.type, sub.compatibilityDate) ?? null;
});
const hasSelection = computed(
  () => selectedGeneral.value || selectedGroup.value !== null || selectedActionItem.value !== null || selectedDropdownItem.value !== null || selectedRegistryItem.value !== null || selectedSubRef.value !== null || selectedRegistrySub.value !== null
);
function setOptionalString(target, key, value) {
  if (value.trim() === "") Reflect.deleteProperty(target, key);
  else target[key] = value;
}
function setOptionalBoolean(target, key, value) {
  if (!value) Reflect.deleteProperty(target, key);
  else target[key] = true;
}
function setOptionalLocale(target, key, value) {
  if (!value || value.length === 0 || value.every((m) => m.message.trim().length === 0)) {
    Reflect.deleteProperty(target, key);
  } else {
    target[key] = value;
  }
}
function emptyLocale() {
  return [{ locale: "zh", message: "" }];
}
function getActionConfigComponent(action) {
  return findButton(action.type, action.compatibilityDate)?.config;
}
function getButtonMetadataLabel(action) {
  return findButton(action.type, action.compatibilityDate)?.metadata.name ?? action.type;
}
function getButtonDeprecationNote(action) {
  const entry = findButton(action.type, action.compatibilityDate);
  if (!entry?.deprecated) return void 0;
  return entry.deprecationNote ?? "\u6B64\u6309\u94AE\u7C7B\u578B\u5DF2\u5F03\u7528\u3002";
}
function patchSelectedGroup(mutate) {
  const sel = selected.value;
  if (sel?.kind !== "group") return;
  const id = sel.id;
  const w = snapshot();
  let touched = false;
  w.groups = w.groups.map((g) => {
    if (g.id !== id) return g;
    const next = { ...g };
    mutate(next);
    touched = true;
    return next;
  });
  if (!touched) return;
  commit(w);
}
function patchSelectedActionItem(mutate) {
  const sel = selected.value;
  if (sel?.kind !== "item") return;
  const id = sel.id;
  const w = snapshot();
  let touched = false;
  w.items = w.items.map((it) => {
    if (it.id !== id || isDropdownItem(it)) return it;
    const next = { ...it };
    mutate(next);
    touched = true;
    return next;
  });
  if (!touched) return;
  commit(w);
}
function patchSelectedRegistryItem(next) {
  const sel = selected.value;
  if (sel?.kind !== "item") return;
  const id = sel.id;
  if (next.id !== id) return;
  const w = snapshot();
  let touched = false;
  w.items = w.items.map((it) => {
    if (it.id !== id || !isRegistryItem(it)) return it;
    touched = true;
    return next;
  });
  if (!touched) return;
  commit(w);
}
function patchSelectedDropdownItem(mutate) {
  const sel = selected.value;
  if (sel?.kind !== "item") return;
  const id = sel.id;
  const w = snapshot();
  let touched = false;
  w.items = w.items.map((it) => {
    if (it.id !== id || !isDropdownItem(it)) return it;
    const next = { ...it };
    mutate(next);
    touched = true;
    return next;
  });
  if (!touched) return;
  commit(w);
}
function patchSelectedSub(mutate) {
  const sel = selected.value;
  if (sel?.kind !== "sub") return;
  const id = sel.id;
  const w = snapshot();
  let touched = false;
  w.items = w.items.map((it) => {
    if (!isDropdownItem(it)) return it;
    if (!it.items.some((s) => s.id === id)) return it;
    return {
      ...it,
      items: it.items.map((s) => {
        if (s.id !== id || isRegistrySub(s)) return s;
        const next = { ...s };
        mutate(next);
        touched = true;
        return next;
      })
    };
  });
  if (!touched) return;
  commit(w);
}
function patchSelectedRegistrySub(next) {
  const sel = selected.value;
  if (sel?.kind !== "sub") return;
  const id = sel.id;
  if (next.id !== id) return;
  const w = snapshot();
  let touched = false;
  w.items = w.items.map((it) => {
    if (!isDropdownItem(it)) return it;
    if (!it.items.some((s) => s.id === id)) return it;
    return {
      ...it,
      items: it.items.map((s) => {
        if (s.id !== id || !isRegistrySub(s)) return s;
        touched = true;
        return next;
      })
    };
  });
  if (!touched) return;
  commit(w);
}
function isDrillableAction(action) {
  if (!action) return false;
  return findButton(action.type, action.compatibilityDate)?.metadata.drillable === true;
}
const selectedDrillableAction = computed(() => {
  const it = selectedActionItem.value;
  if (it?.action && isDrillableAction(it.action)) return it.action;
  const sub = selectedSubRef.value;
  if (sub?.action && isDrillableAction(sub.action)) return sub.action;
  return null;
});
const selectedDrillableTitle = computed(() => {
  const drilled = selectedDrillableAction.value;
  if (!drilled) return null;
  const it = selectedActionItem.value;
  if (it && it.action === drilled) return it.title;
  const sub = selectedSubRef.value;
  if (sub && sub.action === drilled) return sub.title;
  return null;
});
const selectedOperationAncestor = computed(() => {
  const node = selectedActionItem.value ?? selectedSubRef.value;
  if (!node?.action) return null;
  const entry = findButton(node.action.type, node.action.compatibilityDate);
  const operations = entry?.metadata.operations;
  if (!entry || !operations || operations.length === 0) return null;
  return {
    instanceId: node.id,
    // Undefined when the button has no title — the trigger UI then shows the
    // type name alone instead of an empty 「」.
    name: getLocalizedText(node.title, "zh"),
    typeName: entry.metadata.name,
    typeIcon: entry.metadata.icon,
    operations
  };
});
provideEventAncestor(selectedOperationAncestor);
const outerTakeover = inject(SIDEBAR_TAKEOVER_KEY, null);
const outerTakeoverTarget = computed(() => outerTakeover?.target.value ?? null);
const sidebarTakeoverTarget = ref(null);
const drilledByAction = ref(false);
provide(SIDEBAR_TAKEOVER_KEY, { target: sidebarTakeoverTarget, drilled: drilledByAction });
const breadcrumbExt = inject(BREADCRUMB_EXTENSION_KEY, null);
if (breadcrumbExt) {
  const crumb = breadcrumbExt.add();
  watch(
    [selectedDrillableAction, selectedDrillableTitle],
    ([action, title]) => {
      if (!action) {
        crumb.label.value = null;
        crumb.back.value = null;
        return;
      }
      const fallback = findButton(action.type, action.compatibilityDate)?.metadata.name ?? action.type;
      crumb.label.value = getLocalizedText(title ?? void 0, "zh") ?? fallback;
      crumb.back.value = () => {
        selected.value = null;
      };
    },
    { immediate: true }
  );
  onBeforeUnmount(() => {
    crumb.dispose();
  });
}
function selectGeneral() {
  selected.value = { kind: "general" };
}
function selectGroup(id) {
  selected.value = { kind: "group", id };
}
function selectItem(id) {
  selected.value = { kind: "item", id };
}
function selectSub(id) {
  selected.value = { kind: "sub", id };
}
function isSourceDropdown(src) {
  if (src.kind !== ITEM_DRAG_KIND) return false;
  const it = editingItems.value.find((i) => i.id === src.id);
  return !!it && isDropdownItem(it);
}
function findParentDropdownOfSub(items, subId) {
  for (const it of items) {
    if (isDropdownItem(it) && it.items.some((s) => s.id === subId)) return it;
  }
  return null;
}
function actionItemToSubItem(it) {
  const out = {
    id: it.id,
    title: it.title
  };
  if (it.tooltip !== void 0) out.tooltip = it.tooltip;
  if (it.icon !== void 0) out.icon = it.icon;
  if (it.hidden !== void 0) out.hidden = it.hidden;
  if (it.disabled !== void 0) out.disabled = it.disabled;
  if (it.variant !== void 0) out.variant = it.variant;
  if (it.action !== void 0) out.action = it.action;
  return out;
}
function subItemToActionItem(sub, groupId) {
  const out = {
    id: sub.id,
    groupId,
    title: sub.title
  };
  const s = sub;
  if (s.tooltip !== void 0) out.tooltip = s.tooltip;
  if (s.icon !== void 0) out.icon = s.icon;
  if (s.hidden !== void 0) out.hidden = s.hidden;
  if (s.disabled !== void 0) out.disabled = s.disabled;
  if (s.variant !== void 0) out.variant = s.variant;
  if (s.action !== void 0) out.action = s.action;
  return out;
}
function registryItemToSub(it) {
  const { groupId: _drop, ...rest } = it;
  return rest;
}
function registrySubToItem(sub, groupId) {
  return { ...sub, groupId };
}
function materializeAsSubItems(items, kind, id) {
  if (kind === ITEM_DRAG_KIND) {
    const it = items.find((i) => i.id === id);
    if (!it || isDropdownItem(it)) return [];
    if (isRegistryItem(it)) return [registryItemToSub(it)];
    return [actionItemToSubItem(it)];
  }
  if (kind === SUBITEM_DRAG_KIND) {
    const parent = findParentDropdownOfSub(items, id);
    const sub = parent?.items.find((s) => s.id === id);
    return sub ? [{ ...sub }] : [];
  }
  return [];
}
function materializeAsTopLevel(items, kind, id, groupId) {
  if (kind === ITEM_DRAG_KIND) {
    const it = items.find((i) => i.id === id);
    if (!it) return null;
    return { ...it, groupId };
  }
  if (kind === SUBITEM_DRAG_KIND) {
    const parent = findParentDropdownOfSub(items, id);
    const sub = parent?.items.find((s) => s.id === id);
    if (!sub) return null;
    if (isRegistrySub(sub)) return registrySubToItem(sub, groupId);
    return subItemToActionItem(sub, groupId);
  }
  return null;
}
function removeFromSource(w, kind, id) {
  if (kind === ITEM_DRAG_KIND) {
    w.items = w.items.filter((it) => it.id !== id);
    pruneEmptyGroups(w);
    return;
  }
  w.items = w.items.map((it) => {
    if (!isDropdownItem(it)) return it;
    if (!it.items.some((s) => s.id === id)) return it;
    return { ...it, items: it.items.filter((s) => s.id !== id) };
  });
  pruneEmptyDropdowns(w);
  pruneEmptyGroups(w);
}
function pruneEmptyGroups(w) {
  const groupsWithItems = new Set(w.items.map((it) => it.groupId));
  w.groups = w.groups.filter((g) => groupsWithItems.has(g.id));
}
function pruneEmptyDropdowns(w) {
  w.items = w.items.filter((it) => !isDropdownItem(it) || it.items.length > 0);
}
function sortByGroupOrder(groups, items) {
  const order = new Map(groups.map((g, i) => [g.id, i]));
  return [...items].sort((a, b) => (order.get(a.groupId) ?? 0) - (order.get(b.groupId) ?? 0));
}
function dropGroupOnGroup(sourceId, targetId, edge) {
  if (sourceId === targetId) return;
  const w = snapshot();
  const from = w.groups.findIndex((g) => g.id === sourceId);
  const tgt = w.groups.findIndex((g) => g.id === targetId);
  if (from < 0 || tgt < 0) return;
  const [src] = w.groups.splice(from, 1);
  let to = tgt > from ? tgt - 1 : tgt;
  if (edge === "reorder-below") to += 1;
  w.groups.splice(to, 0, src);
  commit(w);
}
function dropAsChild(sourceKind, sourceId, targetItemId) {
  if (sourceKind === GROUP_DRAG_KIND) return;
  if (sourceId === targetItemId) return;
  const w = snapshot();
  const idx = w.items.findIndex((i) => i.id === targetItemId);
  if (idx < 0) return;
  const cur = w.items[idx];
  if (!isDropdownItem(cur)) return;
  if (sourceKind === SUBITEM_DRAG_KIND && cur.items.some((s) => s.id === sourceId)) return;
  const subs = materializeAsSubItems(w.items, sourceKind, sourceId);
  if (subs.length === 0) return;
  removeFromSource(w, sourceKind, sourceId);
  const afterIdx = w.items.findIndex((i) => i.id === targetItemId);
  if (afterIdx < 0) return;
  const target = w.items[afterIdx];
  const updated = { ...target, items: [...target.items, ...subs] };
  w.items = w.items.map((it, i) => i === afterIdx ? updated : it);
  commit(w);
}
function dropAtTopLevel(sourceKind, sourceId, targetItemId, edge) {
  if (sourceKind === GROUP_DRAG_KIND) return;
  if (sourceId === targetItemId) return;
  const w = snapshot();
  const target = w.items.find((i) => i.id === targetItemId);
  if (!target) return;
  const targetGroupId = target.groupId;
  const newItem = materializeAsTopLevel(w.items, sourceKind, sourceId, targetGroupId);
  if (!newItem) return;
  removeFromSource(w, sourceKind, sourceId);
  const next = [];
  let inserted = false;
  for (const it of w.items) {
    if (it.id === targetItemId) {
      if (edge === "reorder-above") {
        next.push(newItem, it);
      } else {
        next.push(it, newItem);
      }
      inserted = true;
    } else {
      next.push(it);
    }
  }
  if (!inserted) next.push(newItem);
  w.items = sortByGroupOrder(w.groups, next);
  pruneEmptyGroups(w);
  pruneEmptyDropdowns(w);
  commit(w);
}
function dropAtSub(sourceKind, sourceId, targetSubId, edge) {
  if (sourceKind === GROUP_DRAG_KIND) return;
  if (sourceId === targetSubId) return;
  const w = snapshot();
  const parent = findParentDropdownOfSub(w.items, targetSubId);
  if (!parent) return;
  const parentId = parent.id;
  const subs = materializeAsSubItems(w.items, sourceKind, sourceId);
  if (subs.length === 0) return;
  removeFromSource(w, sourceKind, sourceId);
  const idx = w.items.findIndex((i) => i.id === parentId);
  if (idx < 0) return;
  const cur = w.items[idx];
  if (!isDropdownItem(cur)) return;
  const newItems = [];
  let inserted = false;
  for (const s of cur.items) {
    if (s.id === targetSubId) {
      if (edge === "reorder-above") newItems.push(...subs, s);
      else newItems.push(s, ...subs);
      inserted = true;
    } else {
      newItems.push(s);
    }
  }
  if (!inserted) newItems.push(...subs);
  const updated = { ...cur, items: newItems };
  w.items = w.items.map((it, i) => i === idx ? updated : it);
  pruneEmptyDropdowns(w);
  pruneEmptyGroups(w);
  commit(w);
}
function addButton(type, compatibilityDate) {
  const groupId = crypto.randomUUID();
  const itemId = crypto.randomUUID();
  const newItem = {
    id: itemId,
    groupId,
    title: emptyLocale(),
    action: { type, compatibilityDate }
  };
  const w = snapshot();
  w.groups = [...w.groups, { id: groupId }];
  w.items = [...w.items, newItem];
  commit(w);
  selected.value = { kind: "item", id: itemId };
}
function seedItemValue(type) {
  if (type === "com.shwfed.actions.item.markdown") {
    return { name: "", content: emptyLocale() };
  }
  return {};
}
function addItem(type, compatibilityDate) {
  const groupId = crypto.randomUUID();
  const itemId = crypto.randomUUID();
  const newItem = {
    id: itemId,
    groupId,
    type,
    compatibilityDate,
    ...seedItemValue(type)
  };
  const w = snapshot();
  w.groups = [...w.groups, { id: groupId }];
  w.items = [...w.items, newItem];
  commit(w);
  selected.value = { kind: "item", id: itemId };
}
function promoteToNewGroup(sourceKind, sourceId, groupInsertIndex) {
  if (sourceKind === GROUP_DRAG_KIND) return;
  const w = snapshot();
  const tmp = materializeAsTopLevel(w.items, sourceKind, sourceId, "");
  if (!tmp) return;
  const newGroup = { id: crypto.randomUUID() };
  const newItem = { ...tmp, groupId: newGroup.id };
  removeFromSource(w, sourceKind, sourceId);
  const idx = Math.max(0, Math.min(groupInsertIndex, w.groups.length));
  w.groups.splice(idx, 0, newGroup);
  w.items = sortByGroupOrder(w.groups, [...w.items, newItem]);
  commit(w);
}
function wrapActionItemInDropdown(itemId) {
  const w = snapshot();
  const idx = w.items.findIndex((i) => i.id === itemId);
  if (idx < 0) return;
  const cur = w.items[idx];
  if (isDropdownItem(cur)) return;
  const sub = actionItemToSubItem(cur);
  const dropdown = {
    id: crypto.randomUUID(),
    groupId: cur.groupId,
    title: emptyLocale(),
    items: [sub]
  };
  w.items = w.items.map((it, i) => i === idx ? dropdown : it);
  commit(w);
  selected.value = { kind: "item", id: dropdown.id };
}
function resolveSelection() {
  const s = selected.value;
  if (!s || s.kind === "general") return;
  if (s.kind === "group" && findGroup(s.id)) return;
  if (s.kind === "item" && findItemNode(s.id)) return;
  if (s.kind === "sub" && findSub(s.id)) return;
  selected.value = { kind: "general" };
}
function deleteGroup(groupId) {
  const w = snapshot();
  w.items = w.items.filter((it) => it.groupId !== groupId);
  w.groups = w.groups.filter((g) => g.id !== groupId);
  commit(w);
  resolveSelection();
}
function deleteItem(itemId) {
  const w = snapshot();
  w.items = w.items.filter((it) => it.id !== itemId);
  pruneEmptyGroups(w);
  commit(w);
  resolveSelection();
}
function deleteSub(subId) {
  const w = snapshot();
  w.items = w.items.map((it) => {
    if (!isDropdownItem(it)) return it;
    if (!it.items.some((s) => s.id === subId)) return it;
    return { ...it, items: it.items.filter((s) => s.id !== subId) };
  });
  pruneEmptyDropdowns(w);
  pruneEmptyGroups(w);
  commit(w);
  resolveSelection();
}
function isKnownSourceKind(k) {
  return k === GROUP_DRAG_KIND || k === ITEM_DRAG_KIND || k === SUBITEM_DRAG_KIND;
}
function onRowDrop({ source, target, instruction }) {
  if (!isKnownSourceKind(source.kind)) return;
  if (target.kind === "group") {
    if (source.kind !== GROUP_DRAG_KIND) return;
    if (instruction === "reorder-above" || instruction === "reorder-below") {
      dropGroupOnGroup(source.id, target.id, instruction);
    }
    return;
  }
  if (target.kind === "item") {
    if (source.kind === GROUP_DRAG_KIND) return;
    if (instruction === "make-child") {
      dropAsChild(source.kind, source.id, target.id);
    } else if (instruction === "reorder-above" || instruction === "reorder-below") {
      dropAtTopLevel(source.kind, source.id, target.id, instruction);
    }
    return;
  }
  if (target.kind === "sub") {
    if (source.kind === GROUP_DRAG_KIND) return;
    if (instruction === "reorder-above" || instruction === "reorder-below") {
      dropAtSub(source.kind, source.id, target.id, instruction);
    }
  }
}
function onZoneDrop({ source, data }) {
  if (!isKnownSourceKind(source.kind) || source.kind === GROUP_DRAG_KIND) return;
  const insertIndex = data.insertIndex;
  if (typeof insertIndex !== "number") return;
  promoteToNewGroup(source.kind, source.id, insertIndex);
}
const dnd = useTreeDnd({ onRowDrop, onZoneDrop });
const pickDragHandle = (el) => el.querySelector(".drag-handle");
function groupRowConfig(groupId) {
  return {
    kind: GROUP_DRAG_KIND,
    canDrop: (src) => src.kind === GROUP_DRAG_KIND,
    blockInstructions: (src) => {
      const block = ["make-child", "reparent"];
      if (src.id === groupId) block.push("reorder-above", "reorder-below");
      return block;
    },
    dragHandle: pickDragHandle
  };
}
function itemRowConfig(itemId, groupId, isDropdown) {
  return {
    kind: ITEM_DRAG_KIND,
    data: { groupId },
    canDrop: (src) => {
      if (src.kind === GROUP_DRAG_KIND) return false;
      if (src.id === itemId) return false;
      if (!isDropdown) return true;
      if (src.kind === SUBITEM_DRAG_KIND) {
        const parent = findParentDropdownOfSub(editingItems.value, src.id);
        if (parent?.id === itemId) return false;
      }
      return true;
    },
    blockInstructions: (src) => {
      const block = ["reparent"];
      if (src.id === itemId) {
        block.push("reorder-above", "reorder-below", "make-child");
        return block;
      }
      if (!isDropdown) {
        block.push("make-child");
        return block;
      }
      block.push("reorder-above");
      if (isSourceDropdown(src)) block.push("make-child");
      return block;
    },
    resolveInstruction: isDropdown ? ({ input, element }) => {
      const rect = element.getBoundingClientRect();
      const relY = input.clientY - rect.top;
      return relY < rect.height / 2 ? "make-child" : "reorder-below";
    } : void 0,
    dragHandle: pickDragHandle
  };
}
function subRowConfig(subId, parentDropdownId) {
  return {
    kind: SUBITEM_DRAG_KIND,
    data: { parentDropdownId },
    canDrop: (src) => src.kind !== GROUP_DRAG_KIND && !isSourceDropdown(src),
    blockInstructions: (src) => {
      const block = ["make-child", "reparent"];
      if (src.id === subId) {
        block.push("reorder-above", "reorder-below");
        return block;
      }
      if (src.kind === SUBITEM_DRAG_KIND) {
        const parent = findParentDropdownOfSub(editingItems.value, src.id);
        if (parent?.id === parentDropdownId) return block;
      }
      block.push("reorder-above", "reorder-below");
      return block;
    },
    dragHandle: pickDragHandle
  };
}
const newGroupZoneConfig = (insertIndex) => ({
  data: { insertIndex },
  canDrop: (src) => src.kind === ITEM_DRAG_KIND || src.kind === SUBITEM_DRAG_KIND
});
const newGroupZoneId = (insertIndex) => `new-group-${insertIndex}`;
</script>

<template>
  <div class="flex flex-col">
    <div class="flex min-h-128 gap-6">
      <!-- Sidebar: teleported into the outer host's left column when one is
           provided (flattens nested sidebars while drilled through e.g.
           modal-layout → form → actions field); otherwise renders inline
           next to the main pane. `:disabled` keeps both modes from a single
           source so DnD refs and reactivity stay intact.

           While a drillable button (e.g. modal-layout) has drilled into one
           of its child blocks, the child owns the entire pane — we drop our
           column entirely so neither an empty teleport-target div nor the
           288px wrapper leaks through as an empty left sidebar. -->
      <Teleport
        v-if="!drilledByAction"
        :to="outerTakeoverTarget"
        :disabled="!outerTakeoverTarget"
      >
        <div
          :class="outerTakeoverTarget ? 'flex flex-1 flex-col gap-2 min-h-0' : 'flex gap-2 w-72 shrink-0 flex-col'"
        >
          <div
            v-if="selectedDrillableAction"
            ref="sidebarTakeoverTarget"
            class="flex flex-1 flex-col min-h-0"
          />
          <template v-else>
            <ScrollArea class="flex-1">
              <div
                class="row general-row pl-2"
                :class="[
  selected?.kind === 'general' ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : 'text-zinc-600 hover:bg-zinc-50'
]"
                @click.stop="selectGeneral()"
              >
                <Icon
                  icon="fluent:settings-20-regular"
                  class="size-4 shrink-0"
                />
                <span class="flex-1 truncate">通用</span>
              </div>

              <div
                :ref="dnd.zoneRef(newGroupZoneId(0), newGroupZoneConfig(0))"
                class="new-group-zone"
                :data-active="dnd.isZoneActive(newGroupZoneId(0)) ? 'true' : void 0"
              >
                <span class="new-group-zone-label">＋ 新按钮组</span>
              </div>

              <template
                v-for="group in editingGroups"
                :key="group.id"
              >
                <div class="space-y-1">
                  <div
                    :ref="dnd.rowRef(group.id, groupRowConfig(group.id))"
                    class="row group group-row pl-2"
                    :class="[
  selected?.kind === 'group' && selected.id === group.id ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : ''
]"
                    :data-instruction="dnd.instructionFor(group.id) ?? void 0"
                    @click.stop="selectGroup(group.id)"
                  >
                    <Icon
                      icon="fluent:re-order-dots-vertical-20-regular"
                      class="drag-handle size-4 shrink-0 cursor-grab text-zinc-400"
                    />
                    <span class="flex-1 truncate">
                      {{ composeGroupLabel(group.id) }}
                    </span>
                    <Tooltip :delay-duration="180">
                      <TooltipTrigger as-child>
                        <button
                          class="shrink-0 text-zinc-300 opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
                          @click.stop="deleteGroup(group.id)"
                        >
                          <Icon
                            icon="fluent:delete-20-regular"
                            class="size-3.5"
                          />
                        </button>
                      </TooltipTrigger>
                      <TooltipContent side="right">
                        删除该按钮组及其所有按钮
                      </TooltipContent>
                    </Tooltip>
                  </div>

                  <div
                    v-for="item in itemsOfGroup(group.id)"
                    :key="item.id"
                    :ref="dnd.rowRef(item.id, itemRowConfig(item.id, item.groupId, isDropdownItem(item)))"
                    class="item-wrap space-y-1"
                    :class="{ 'dropdown-wrap': isDropdownItem(item) }"
                    :data-instruction="dnd.instructionFor(item.id) ?? void 0"
                  >
                    <div
                      class="row group item-row pl-5"
                      :class="[
  selected?.kind === 'item' && selected.id === item.id ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : 'text-zinc-600 hover:bg-zinc-50'
]"
                      @click.stop="selectItem(item.id)"
                    >
                      <Icon
                        icon="fluent:re-order-dots-vertical-20-regular"
                        class="drag-handle size-4 shrink-0 cursor-grab text-zinc-300"
                      />
                      <Icon
                        v-if="itemIcon(item)"
                        :icon="itemIcon(item)"
                        class="size-4 shrink-0"
                        :class="isRegistryItem(item) ? 'text-purple-500' : void 0"
                      />
                      <span class="flex-1 truncate">{{ itemLabel(item) }}</span>
                      <Tooltip
                        v-if="!isDropdownItem(item) && !isRegistryItem(item)"
                        :delay-duration="180"
                      >
                        <TooltipTrigger as-child>
                          <button
                            class="shrink-0 text-zinc-300 opacity-0 transition-opacity hover:text-zinc-600 group-hover:opacity-100"
                            @click.stop="wrapActionItemInDropdown(item.id)"
                          >
                            <Icon
                              icon="fluent:folder-add-20-regular"
                              class="size-3.5"
                            />
                          </button>
                        </TooltipTrigger>
                        <TooltipContent side="right">
                          将该按钮包裹为下拉按钮，之后可添加更多下拉项
                        </TooltipContent>
                      </Tooltip>
                      <Tooltip :delay-duration="180">
                        <TooltipTrigger as-child>
                          <button
                            class="shrink-0 text-zinc-300 opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
                            @click.stop="deleteItem(item.id)"
                          >
                            <Icon
                              icon="fluent:delete-20-regular"
                              class="size-3.5"
                            />
                          </button>
                        </TooltipTrigger>
                        <TooltipContent side="right">
                          {{ isDropdownItem(item) ? "\u5220\u9664\u8BE5\u4E0B\u62C9\u6309\u94AE\u53CA\u5176\u6240\u6709\u4E0B\u62C9\u9879" : isRegistryItem(item) ? "\u5220\u9664\u8BE5\u9879" : "\u5220\u9664\u8BE5\u6309\u94AE" }}
                        </TooltipContent>
                      </Tooltip>
                    </div>

                    <template v-if="isDropdownItem(item)">
                      <div
                        v-for="sub in item.items"
                        :key="sub.id"
                        :ref="dnd.rowRef(sub.id, subRowConfig(sub.id, item.id))"
                        class="row group sub-item-row pl-10"
                        :class="[
  selected?.kind === 'sub' && selected.id === sub.id ? 'bg-[color-mix(in_srgb,var(--primary)_10%,white)] text-(--primary)' : 'text-zinc-500 hover:bg-zinc-50'
]"
                        :data-instruction="dnd.instructionFor(sub.id) ?? void 0"
                        @click.stop="selectSub(sub.id)"
                      >
                        <Icon
                          icon="fluent:re-order-dots-vertical-20-regular"
                          class="drag-handle size-4 shrink-0 cursor-grab text-zinc-300"
                        />
                        <Icon
                          v-if="subIcon(sub)"
                          :icon="subIcon(sub)"
                          class="size-4 shrink-0"
                          :class="isRegistrySub(sub) ? 'text-purple-500' : void 0"
                        />
                        <span class="flex-1 truncate">{{ subLabel(sub) }}</span>
                        <Tooltip :delay-duration="180">
                          <TooltipTrigger as-child>
                            <button
                              class="shrink-0 text-zinc-300 opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
                              @click.stop="deleteSub(sub.id)"
                            >
                              <Icon
                                icon="fluent:delete-20-regular"
                                class="size-3.5"
                              />
                            </button>
                          </TooltipTrigger>
                          <TooltipContent side="right">
                            从下拉菜单中移除该按钮
                          </TooltipContent>
                        </Tooltip>
                      </div>
                    </template>
                  </div>
                </div>
              </template>

              <div
                :ref="dnd.zoneRef(newGroupZoneId(editingGroups.length), newGroupZoneConfig(editingGroups.length))"
                class="new-group-zone"
                :data-active="dnd.isZoneActive(newGroupZoneId(editingGroups.length)) ? 'true' : void 0"
              >
                <span class="new-group-zone-label">＋ 新按钮组</span>
              </div>
            </ScrollArea>

            <div class="border-t border-zinc-100 p-2">
              <InputGroup>
                <InputGroupAddon align="inline-start">
                  <Icon icon="fluent:add-20-regular" />
                </InputGroupAddon>
                <InputGroupCombobox>
                  <CommandGroup>
                    <CommandItem
                      v-for="entry in ACTION_PICKER_ENTRIES"
                      :key="entry.type"
                      :value="entry.type"
                      @select="addButton(entry.type, entry.compatibilityDate)"
                    >
                      <Icon
                        :icon="entry.icon"
                        class="size-4 shrink-0"
                      />
                      <span class="flex-1">{{ entry.name }}</span>
                      <span class="ml-auto text-xs tabular-nums text-zinc-400">{{ entry.compatibilityDate }}</span>
                    </CommandItem>
                    <CommandItem
                      v-for="entry in ITEM_PICKER_ENTRIES"
                      :key="entry.type"
                      :value="entry.type"
                      @select="addItem(entry.type, entry.compatibilityDate)"
                    >
                      <Icon
                        :icon="entry.icon"
                        class="size-4 shrink-0 text-purple-500"
                      />
                      <span class="flex-1">{{ entry.name }}</span>
                      <span class="ml-auto text-xs tabular-nums text-zinc-400">{{ entry.compatibilityDate }}</span>
                    </CommandItem>
                  </CommandGroup>
                </InputGroupCombobox>
              </InputGroup>
            </div>
          </template>
        </div>
      </Teleport>

      <div class="flex flex-1 flex-col">
        <ScrollArea class="flex-1">
          <div
            v-if="!hasSelection"
            class="flex min-h-112 items-center justify-center p-8 text-sm text-zinc-400"
          >
            选择一项以编辑
          </div>

          <div
            v-else-if="selectedGeneral"
            class="flex flex-col gap-4 p-6"
          >
            <slot name="general-extra" />

            <div class="grid grid-cols-2 gap-4">
              <Field orientation="vertical">
                <FieldLabel class="text-xs text-zinc-500">
                  <template
                    v-if="generalFieldDescription('size')"
                    #tooltip
                  >
                    <Markdown
                      :source="generalFieldDescription('size')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ generalFieldTitle("size") }}
                </FieldLabel>
                <Select
                  :model-value="editingSize"
                  @update:model-value="(v) => editingSize = v"
                >
                  <SelectTrigger class="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem
                      v-for="opt in SIZE_OPTIONS"
                      :key="opt.value"
                      :value="opt.value"
                    >
                      {{ opt.label }}
                    </SelectItem>
                  </SelectContent>
                </Select>
              </Field>

              <Field orientation="vertical">
                <FieldLabel class="text-xs text-zinc-500">
                  <template
                    v-if="generalFieldDescription('gap')"
                    #tooltip
                  >
                    <Markdown
                      :source="generalFieldDescription('gap')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ generalFieldTitle("gap") }}
                </FieldLabel>
                <InputGroup>
                  <InputGroupNumberField
                    :model-value="editingGap"
                    :min="0"
                    @update:model-value="(v) => editingGap = v"
                  />
                </InputGroup>
              </Field>
            </div>

            <Field orientation="vertical">
              <FieldLabel class="text-xs text-zinc-500">
                <template
                  v-if="generalFieldDescription('style')"
                  #tooltip
                >
                  <Markdown
                    :source="generalFieldDescription('style')"
                    block
                    class="prose prose-sm prose-zinc"
                  />
                </template>
                {{ generalFieldTitle("style") }}
              </FieldLabel>
              <Textarea
                :model-value="editingStyle"
                placeholder="CSS 样式字符串"
                class="min-h-20 font-mono text-xs"
                @update:model-value="(v) => editingStyle = String(v)"
              />
            </Field>
          </div>

          <div
            v-else-if="selectedGroup"
            class="flex flex-col gap-4 p-6"
          >
            <Field orientation="vertical">
              <FieldLabel class="text-xs text-zinc-500">
                <template
                  v-if="groupFieldDescription('variant')"
                  #tooltip
                >
                  <Markdown
                    :source="groupFieldDescription('variant')"
                    block
                    class="prose prose-sm prose-zinc"
                  />
                </template>
                {{ groupFieldTitle("variant") }}
              </FieldLabel>
              <Select
                :model-value="selectedGroup.variant ?? 'default'"
                @update:model-value="(v) => patchSelectedGroup((g) => {
  g.variant = v;
})"
              >
                <SelectTrigger class="w-full">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem
                    v-for="opt in VARIANT_OPTIONS"
                    :key="opt.value"
                    :value="opt.value"
                  >
                    {{ opt.label }}
                  </SelectItem>
                </SelectContent>
              </Select>
            </Field>
          </div>

          <div
            v-else-if="selectedRegistryItem"
            class="flex flex-col gap-4 p-6"
          >
            <component
              :is="selectedRegistryEntry?.config"
              v-if="selectedRegistryEntry"
              :model-value="selectedRegistryItem"
              @update:model-value="(v) => patchSelectedRegistryItem(v)"
            />
            <div
              v-else
              class="flex items-start gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800"
              role="alert"
            >
              <Icon
                icon="fluent:warning-20-regular"
                class="mt-px size-4 shrink-0"
              />
              <span class="flex-1">未找到匹配的项目类型：{{ selectedRegistryItem.type }}@{{ selectedRegistryItem.compatibilityDate }}</span>
            </div>
          </div>

          <div
            v-else-if="selectedDropdownItem"
            class="flex flex-col gap-4 p-6"
          >
            <Field orientation="vertical">
              <FieldLabel class="text-xs text-zinc-500">
                <template
                  v-if="dropdownItemFieldDescription('title')"
                  #tooltip
                >
                  <Markdown
                    :source="dropdownItemFieldDescription('title')"
                    block
                    class="prose prose-sm prose-zinc"
                  />
                </template>
                {{ dropdownItemFieldTitle("title") }}
              </FieldLabel>
              <LocaleField
                :model-value="selectedDropdownItem.title"
                @update:model-value="(v) => patchSelectedDropdownItem((it) => {
  it.title = v;
})"
              />
            </Field>

            <div class="flex items-end gap-4">
              <Field
                orientation="vertical"
                class="flex-1"
              >
                <FieldLabel class="text-xs text-zinc-500">
                  <template
                    v-if="dropdownItemFieldDescription('icon')"
                    #tooltip
                  >
                    <Markdown
                      :source="dropdownItemFieldDescription('icon')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ dropdownItemFieldTitle("icon") }}
                </FieldLabel>
                <IconPicker
                  :model-value="selectedDropdownItem.icon ?? ''"
                  @update:model-value="(v) => patchSelectedDropdownItem((it) => setOptionalString(it, 'icon', v))"
                />
              </Field>
              <Field
                orientation="horizontal"
                class="h-9 w-auto gap-2"
              >
                <Switch
                  :model-value="selectedDropdownItem.hideTitle ?? false"
                  @update:model-value="(v) => patchSelectedDropdownItem((it) => setOptionalBoolean(it, 'hideTitle', v))"
                />
                <FieldLabel class="text-sm text-zinc-600">
                  <template
                    v-if="dropdownItemFieldDescription('hideTitle')"
                    #tooltip
                  >
                    <Markdown
                      :source="dropdownItemFieldDescription('hideTitle')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ dropdownItemFieldTitle("hideTitle") }}
                </FieldLabel>
              </Field>
            </div>

            <p class="text-xs text-zinc-400">
              下拉按钮的下拉项以独立行的形式编辑，选中任意下拉项即可修改其属性。
            </p>
          </div>

          <div
            v-else-if="selectedActionItem"
            :class="drilledByAction && isDrillableAction(selectedActionItem.action) ? 'flex flex-col' : 'flex flex-col gap-4 p-6'"
          >
            <template v-if="!(drilledByAction && isDrillableAction(selectedActionItem.action))">
              <div class="grid grid-cols-2 gap-4">
                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="itemFieldDescription('title')"
                      #tooltip
                    >
                      <Markdown
                        :source="itemFieldDescription('title')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ itemFieldTitle("title") }}
                  </FieldLabel>
                  <LocaleField
                    :model-value="selectedActionItem.title"
                    @update:model-value="(v) => patchSelectedActionItem((it) => {
  it.title = v;
})"
                  />
                </Field>

                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="itemFieldDescription('tooltip')"
                      #tooltip
                    >
                      <Markdown
                        :source="itemFieldDescription('tooltip')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ itemFieldTitle("tooltip") }}
                  </FieldLabel>
                  <LocaleField
                    markdown
                    :model-value="selectedActionItem.tooltip ?? emptyLocale()"
                    @update:model-value="(v) => patchSelectedActionItem((it) => setOptionalLocale(it, 'tooltip', v))"
                  />
                </Field>
              </div>

              <div class="grid grid-cols-2 gap-4">
                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="itemFieldDescription('hidden')"
                      #tooltip
                    >
                      <Markdown
                        :source="itemFieldDescription('hidden')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ itemFieldTitle("hidden") }}
                  </FieldLabel>
                  <ExpressionEditor
                    :model-value="selectedActionItem.hidden ?? ''"
                    placeholder="CEL 表达式，返回 true 时隐藏"
                    result-type="bool"
                    class="min-h-10"
                    @update:model-value="(v) => patchSelectedActionItem((it) => setOptionalString(it, 'hidden', v))"
                  />
                </Field>

                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="itemFieldDescription('disabled')"
                      #tooltip
                    >
                      <Markdown
                        :source="itemFieldDescription('disabled')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ itemFieldTitle("disabled") }}
                  </FieldLabel>
                  <ExpressionEditor
                    :model-value="selectedActionItem.disabled ?? ''"
                    placeholder="CEL 表达式，返回 true 时禁用"
                    result-type="bool"
                    class="min-h-10"
                    @update:model-value="(v) => patchSelectedActionItem((it) => setOptionalString(it, 'disabled', v))"
                  />
                </Field>
              </div>

              <div class="flex items-end gap-4">
                <Field
                  orientation="vertical"
                  class="w-1/2"
                >
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="itemFieldDescription('variant')"
                      #tooltip
                    >
                      <Markdown
                        :source="itemFieldDescription('variant')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ itemFieldTitle("variant") }}
                  </FieldLabel>
                  <Select
                    :model-value="selectedActionItem.variant ?? 'default'"
                    @update:model-value="(v) => patchSelectedActionItem((it) => {
  it.variant = v;
})"
                  >
                    <SelectTrigger class="w-full">
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem
                        v-for="opt in VARIANT_OPTIONS"
                        :key="opt.value"
                        :value="opt.value"
                      >
                        {{ opt.label }}
                      </SelectItem>
                    </SelectContent>
                  </Select>
                </Field>
                <div class="flex w-1/2 items-end gap-4">
                  <Field
                    orientation="vertical"
                    class="flex-1"
                  >
                    <FieldLabel class="text-xs text-zinc-500">
                      <template
                        v-if="itemFieldDescription('icon')"
                        #tooltip
                      >
                        <Markdown
                          :source="itemFieldDescription('icon')"
                          block
                          class="prose prose-sm prose-zinc"
                        />
                      </template>
                      {{ itemFieldTitle("icon") }}
                    </FieldLabel>
                    <IconPicker
                      :model-value="selectedActionItem.icon ?? ''"
                      @update:model-value="(v) => patchSelectedActionItem((it) => setOptionalString(it, 'icon', v))"
                    />
                  </Field>
                  <Field
                    orientation="horizontal"
                    class="h-9 w-auto gap-2"
                  >
                    <Switch
                      :model-value="selectedActionItem.hideTitle ?? false"
                      @update:model-value="(v) => patchSelectedActionItem((it) => setOptionalBoolean(it, 'hideTitle', v))"
                    />
                    <FieldLabel class="text-sm text-zinc-600">
                      <template
                        v-if="itemFieldDescription('hideTitle')"
                        #tooltip
                      >
                        <Markdown
                          :source="itemFieldDescription('hideTitle')"
                          block
                          class="prose prose-sm prose-zinc"
                        />
                      </template>
                      {{ itemFieldTitle("hideTitle") }}
                    </FieldLabel>
                  </Field>
                </div>
              </div>
            </template>

            <template v-if="selectedActionItem.action">
              <template v-if="!isDrillableAction(selectedActionItem.action)">
                <Separator />

                <div class="text-xs text-zinc-400">
                  {{ getButtonMetadataLabel(selectedActionItem.action) }}
                </div>

                <div
                  v-if="getButtonDeprecationNote(selectedActionItem.action)"
                  class="flex items-start gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800"
                  role="alert"
                >
                  <Icon
                    icon="fluent:warning-20-regular"
                    class="mt-px size-4 shrink-0"
                  />
                  <span class="flex-1">{{ getButtonDeprecationNote(selectedActionItem.action) }}</span>
                </div>
              </template>

              <component
                :is="getActionConfigComponent(selectedActionItem.action)"
                v-if="getActionConfigComponent(selectedActionItem.action)"
                :model-value="selectedActionItem.action"
                :configure="configure"
                @update:model-value="(v) => patchSelectedActionItem((it) => {
  it.action = v;
})"
              />
            </template>
          </div>

          <div
            v-else-if="selectedSubRef"
            :class="drilledByAction && isDrillableAction(selectedSubRef.action) ? 'flex flex-col' : 'flex flex-col gap-4 p-6'"
          >
            <template v-if="!(drilledByAction && isDrillableAction(selectedSubRef.action))">
              <div class="grid grid-cols-2 gap-4">
                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="subFieldDescription('title')"
                      #tooltip
                    >
                      <Markdown
                        :source="subFieldDescription('title')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ subFieldTitle("title") }}
                  </FieldLabel>
                  <LocaleField
                    :model-value="selectedSubRef.title"
                    @update:model-value="(v) => patchSelectedSub((s) => {
  s.title = v;
})"
                  />
                </Field>

                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="subFieldDescription('tooltip')"
                      #tooltip
                    >
                      <Markdown
                        :source="subFieldDescription('tooltip')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ subFieldTitle("tooltip") }}
                  </FieldLabel>
                  <LocaleField
                    markdown
                    :model-value="selectedSubRef.tooltip ?? emptyLocale()"
                    @update:model-value="(v) => patchSelectedSub((s) => setOptionalLocale(s, 'tooltip', v))"
                  />
                </Field>
              </div>

              <div class="grid grid-cols-2 gap-4">
                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="subFieldDescription('hidden')"
                      #tooltip
                    >
                      <Markdown
                        :source="subFieldDescription('hidden')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ subFieldTitle("hidden") }}
                  </FieldLabel>
                  <ExpressionEditor
                    :model-value="selectedSubRef.hidden ?? ''"
                    placeholder="CEL 表达式，返回 true 时隐藏"
                    result-type="bool"
                    class="min-h-10"
                    @update:model-value="(v) => patchSelectedSub((s) => setOptionalString(s, 'hidden', v))"
                  />
                </Field>

                <Field orientation="vertical">
                  <FieldLabel class="text-xs text-zinc-500">
                    <template
                      v-if="subFieldDescription('disabled')"
                      #tooltip
                    >
                      <Markdown
                        :source="subFieldDescription('disabled')"
                        block
                        class="prose prose-sm prose-zinc"
                      />
                    </template>
                    {{ subFieldTitle("disabled") }}
                  </FieldLabel>
                  <ExpressionEditor
                    :model-value="selectedSubRef.disabled ?? ''"
                    placeholder="CEL 表达式，返回 true 时禁用"
                    result-type="bool"
                    class="min-h-10"
                    @update:model-value="(v) => patchSelectedSub((s) => setOptionalString(s, 'disabled', v))"
                  />
                </Field>
              </div>

              <Field orientation="vertical">
                <FieldLabel class="text-xs text-zinc-500">
                  <template
                    v-if="subFieldDescription('icon')"
                    #tooltip
                  >
                    <Markdown
                      :source="subFieldDescription('icon')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ subFieldTitle("icon") }}
                </FieldLabel>
                <IconPicker
                  :model-value="selectedSubRef.icon ?? ''"
                  @update:model-value="(v) => patchSelectedSub((s) => setOptionalString(s, 'icon', v))"
                />
              </Field>

              <Field orientation="vertical">
                <FieldLabel class="text-xs text-zinc-500">
                  <template
                    v-if="subFieldDescription('variant')"
                    #tooltip
                  >
                    <Markdown
                      :source="subFieldDescription('variant')"
                      block
                      class="prose prose-sm prose-zinc"
                    />
                  </template>
                  {{ subFieldTitle("variant") }}
                </FieldLabel>
                <Select
                  :model-value="selectedSubRef.variant ?? 'default'"
                  @update:model-value="(v) => patchSelectedSub((s) => {
  s.variant = v;
})"
                >
                  <SelectTrigger class="w-1/2">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem
                      v-for="opt in VARIANT_OPTIONS"
                      :key="opt.value"
                      :value="opt.value"
                    >
                      {{ opt.label }}
                    </SelectItem>
                  </SelectContent>
                </Select>
              </Field>
            </template>

            <template v-if="selectedSubRef.action">
              <template v-if="!isDrillableAction(selectedSubRef.action)">
                <Separator />

                <div class="text-xs text-zinc-400">
                  {{ getButtonMetadataLabel(selectedSubRef.action) }}
                </div>

                <div
                  v-if="getButtonDeprecationNote(selectedSubRef.action)"
                  class="flex items-start gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800"
                  role="alert"
                >
                  <Icon
                    icon="fluent:warning-20-regular"
                    class="mt-px size-4 shrink-0"
                  />
                  <span class="flex-1">{{ getButtonDeprecationNote(selectedSubRef.action) }}</span>
                </div>
              </template>

              <component
                :is="getActionConfigComponent(selectedSubRef.action)"
                v-if="getActionConfigComponent(selectedSubRef.action)"
                :model-value="selectedSubRef.action"
                :configure="configure"
                @update:model-value="(v) => patchSelectedSub((s) => {
  s.action = v;
})"
              />
            </template>
          </div>

          <div
            v-else-if="selectedRegistrySub"
            class="flex flex-col gap-4 p-6"
          >
            <component
              :is="selectedRegistrySubEntry?.config"
              v-if="selectedRegistrySubEntry"
              :model-value="selectedRegistrySub"
              @update:model-value="(v) => patchSelectedRegistrySub(v)"
            />
            <div
              v-else
              class="flex items-start gap-2 rounded border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800"
              role="alert"
            >
              <Icon
                icon="fluent:warning-20-regular"
                class="mt-px size-4 shrink-0"
              />
              <span class="flex-1">未找到匹配的项目类型：{{ selectedRegistrySub.type }}@{{ selectedRegistrySub.compatibilityDate }}</span>
            </div>
          </div>
        </ScrollArea>
      </div>
    </div>
  </div>
</template>

<style scoped>
.row{align-items:center;border-radius:.25rem;cursor:pointer;display:flex;font-size:.875rem;gap:.375rem;padding-block:.375rem;padding-right:.5rem;position:relative;transition:background-color .1s ease;-webkit-user-select:none;-moz-user-select:none;user-select:none}.group-row{color:#3f3f46;font-weight:500}.group-row:hover{background:#fafafa}.general-row{cursor:pointer;font-weight:500}[data-instruction=reorder-above]:before,[data-instruction=reorder-below]:after{background:var(--primary,#2563eb);content:"";height:2px;left:.5rem;pointer-events:none;position:absolute;right:.5rem;z-index:1}[data-instruction=reorder-above]:before{top:-1px}[data-instruction=reorder-below]:after{bottom:-1px}.item-wrap{position:relative}.dropdown-wrap[data-instruction=make-child]{background:color-mix(in srgb,var(--primary,#2563eb) 12%,#fff);border-radius:.25rem;outline:1px dashed var(--primary,#2563eb);outline-offset:-2px}.new-group-zone{align-items:center;border-radius:.25rem;color:transparent;display:flex;font-size:.75rem;height:.5rem;justify-content:center;margin:.125rem 0;pointer-events:auto;transition:height .12s ease,color .12s ease,background-color .12s ease}.new-group-zone[data-active=true]{background-color:color-mix(in srgb,var(--primary,#2563eb) 8%,#fff);color:var(--primary,#2563eb);height:1.5rem}.new-group-zone-label{pointer-events:none}
</style>
