import { h, r as registerInstance, c as createEvent } from './index-BIwHMk6j.js';
import { b as n } from './index.esm-CMeGYws2.js';

/**
 * Remove the node at `path`.
 *
 * Removing the root yields an empty `all`-group so the tree always has
 * something to render. Returns `rule` unchanged when `path` does not
 * address a removable node.
 * @param rule
 * @param path
 */
function deleteNode(rule, path) {
    if (path.length === 0) {
        return { type: 'all', rules: [] };
    }
    const parentPath = path.slice(0, -1);
    const key = path.at(-1);
    const parent = getNode(rule, parentPath);
    if ((parent.type === 'all' || parent.type === 'any') &&
        typeof key === 'number') {
        const rules = [...parent.rules];
        rules.splice(key, 1);
        return replaceAt(rule, parentPath, Object.assign(Object.assign({}, parent), { rules }));
    }
    if (parent.type === 'not' && key === 'rule') {
        return replaceAt(rule, parentPath, { type: 'all', rules: [] });
    }
    return rule;
}
/**
 * Replace the `args` of a `ref` node. Returns `rule` unchanged when
 * `path` does not resolve to a `ref`.
 * @param rule
 * @param path
 * @param args
 */
function updateArgs(rule, path, args) {
    const node = getNode(rule, path);
    if (node.type !== 'ref') {
        return rule;
    }
    return replaceAt(rule, path, Object.assign(Object.assign({}, node), { args }));
}
/**
 * Replace the node at `path` with `replacement`, returning a new rule
 * tree. Used by callers that need to apply a non-atomic change as a
 * single emit (e.g. updating a group's whole `rules` array at once).
 * @param rule
 * @param path
 * @param replacement
 */
function replaceNode(rule, path, replacement) {
    return replaceAt(rule, path, replacement);
}
/**
 * Wrap `group` in a `not` when `isNegated`, otherwise return it
 * unchanged. Used when re-emitting a group whose negation flag should
 * be preserved.
 * @param group
 * @param isNegated
 */
function wrapNegated(group, isNegated) {
    return isNegated ? { type: 'not', rule: group } : group;
}
/**
 * Append `child` to a group's `rules`, returning a new group. The
 * input is not mutated.
 * @param group
 * @param child
 */
function appendChild(group, child) {
    return Object.assign(Object.assign({}, group), { rules: [...group.rules, child] });
}
/**
 * Structural equality on two paths.
 * @param a
 * @param b
 */
function pathsEqual(a, b) {
    if (a.length !== b.length) {
        return false;
    }
    return a.every((value, index) => value === b[index]);
}
/**
 * Filter validation issues to those whose `path` matches `path`.
 * @param issues
 * @param path
 */
function issuesAt(issues, path) {
    return issues.filter((issue) => pathsEqual(issue.path, path));
}
function getNode(rule, path) {
    let node = rule;
    for (const key of path) {
        node = stepIntoNode(node, key);
    }
    return node;
}
function replaceAt(root, path, replacement) {
    if (path.length === 0) {
        return replacement;
    }
    const [head, ...tail] = path;
    if (root.type === 'not' && head === 'rule') {
        return Object.assign(Object.assign({}, root), { rule: replaceAt(root.rule, tail, replacement) });
    }
    if ((root.type === 'all' || root.type === 'any') && head === 'rules') {
        if (tail.length === 0) {
            return replacement;
        }
        const [index, ...rest] = tail;
        if (typeof index !== 'number') {
            return root;
        }
        const rules = [...root.rules];
        rules[index] = replaceAt(rules[index], rest, replacement);
        return Object.assign(Object.assign({}, root), { rules });
    }
    return root;
}
function stepIntoNode(node, key) {
    if (node.type === 'not' && key === 'rule') {
        return node.rule;
    }
    if ((node.type === 'all' || node.type === 'any') && key === 'rules') {
        return node;
    }
    if ((node.type === 'all' || node.type === 'any') &&
        typeof key === 'number') {
        return node.rules[key];
    }
    return node;
}

const UNKNOWN_ICON = {
    name: 'question',
    color: 'rgb(var(--color-glaucous-light))',
};
/**
 * Class map for a rule-node container. Adds the type modifier and an
 * `invalid` flag when validation issues attach to this node.
 * @param type
 * @param issues
 */
function nodeClasses(type, issues) {
    return {
        'rule-node': true,
        [`rule-node--${type}`]: true,
        'rule-node--invalid': issues.length > 0,
    };
}
/**
 * Build the searchable list shown by the primitive pickers. Cached on
 * the render context so it isn't recomputed per node. `meta.title` and
 * `meta.description` are passed through the translator so callers can
 * register translation keys as metadata — plain strings travel through
 * unchanged.
 * @param metadataById
 * @param translator
 */
function createPrimitiveOptions(metadataById, translator) {
    return [...metadataById.values()]
        .map((meta) => ({
        value: meta.id,
        text: meta.title ? translator.get(meta.title) : meta.id,
        secondaryText: meta.description
            ? translator.get(meta.description)
            : '',
        icon: meta.icon,
    }))
        .sort((a, b) => a.text.localeCompare(b.text));
}
/**
 * Find the picker item for `id`, falling back to a placeholder marked
 * with the unknown-primitive icon when the id isn't in the registry.
 * @param allItems
 * @param id
 */
function resolveItem(allItems, id) {
    var _a;
    return ((_a = allItems.find((item) => item.value === id)) !== null && _a !== void 0 ? _a : {
        value: id,
        text: id,
        icon: UNKNOWN_ICON,
    });
}
/**
 * Build a picker `searcher` bound to a fixed item list. Both the ref
 * picker and the chip picker want exactly this — wrapping
 * {@link searchItems} in a promise — and deserve to share it.
 * @param items
 */
function createSearcher(items) {
    return (query) => Promise.resolve(searchItems(items, query));
}
function searchItems(allItems, query) {
    if (!query) {
        return allItems;
    }
    const parts = query.toLocaleLowerCase().split(/\s+/);
    return allItems.filter((item) => parts.every((part) => {
        var _a, _b;
        return item.text.toLocaleLowerCase().includes(part) ||
            ((_a = item.secondaryText) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase().includes(part)) ||
            ((_b = item.value) === null || _b === void 0 ? void 0 : _b.toLocaleLowerCase().includes(part));
    }));
}

const CHIP_TOKEN_PREFIX = '__chip__:';
function chipTokenFor(index) {
    return `${CHIP_TOKEN_PREFIX}${index}`;
}
// The address of a chip inside a group is the group's path followed
// by a two-segment suffix `['rules', index]`. Build and parse via
// these helpers so both sides stay in sync.
const CHIP_PATH_SUFFIX_LENGTH = 2;
function buildChipPath(groupPath, index) {
    return [...groupPath, 'rules', index];
}
function parentGroupPath(chipPath) {
    return chipPath.slice(0, -CHIP_PATH_SUFFIX_LENGTH);
}
/**
 * If `node` is a `ref` or a `not(ref)`, return the underlying ref
 * together with its negation flag. Used to decide whether a child of a
 * group renders as a chip (this returns non-null) or as a nested box
 * (this returns null).
 * @param node
 */
function extractRef(node) {
    if (node.type === 'ref') {
        return { ref: node, isNegated: false };
    }
    if (node.type === 'not' && node.rule.type === 'ref') {
        return { ref: node.rule, isNegated: true };
    }
    return null;
}
const GroupChipsView = ({ groupNode, groupPath, chipChildren, ctx }) => {
    const chipsByToken = new Map();
    const value = chipChildren.map((entry) => {
        const token = chipTokenFor(entry.index);
        chipsByToken.set(token, entry);
        const base = resolveItem(ctx.primitiveOptions, entry.ref.id);
        return Object.assign(Object.assign({}, base), { text: entry.isNegated ? `¬ ${base.text}` : base.text, value: token });
    });
    const searcher = createSearcher(ctx.primitiveOptions);
    const openChip = findOpenChip(chipChildren, groupPath, ctx.openChipPath);
    const delimiter = groupNode.type === 'all' ? '&' : '|';
    return (h("limel-popover", { class: "rule-node__chips-popover", open: openChip !== null, openDirection: "bottom-start", onClose: ctx.onPopoverClose }, h("div", { slot: "trigger", class: "rule-node__chips" }, h("limel-picker", { multiple: true, label: ctx.translator.get('webclient.rule-editor.rules-label'), value: value, searcher: searcher, delimiter: delimiter, readonly: ctx.readonly, disabled: ctx.disabled, onChange: chipsChangeHandler(groupNode, groupPath, chipsByToken, ctx), onInteract: chipInteractHandler(groupPath, chipsByToken, ctx) })), h("div", null, renderChipPopoverContent(openChip, groupPath, ctx))));
};
function renderChipPopoverContent(openChip, groupPath, ctx) {
    if (!openChip) {
        return null;
    }
    const openChipPath = buildChipPath(groupPath, openChip.index);
    return (h("limebb-rule-chip-popover", { platform: ctx.platform, context: ctx.context, refNode: openChip.ref, isNegated: openChip.isNegated, metadata: ctx.metadataById.get(openChip.ref.id), readonly: ctx.readonly, disabled: ctx.disabled, isMutable: ctx.isMutable, onNegate: chipNegateHandler(openChip, openChipPath, ctx), onUpdateArgs: chipArgsHandler(openChip, openChipPath, ctx), onDeleteChip: chipDeleteHandler(openChipPath, ctx) }));
}
const chipsChangeHandler = (groupNode, groupPath, chipsByToken, ctx) => (event) => {
    event.stopPropagation();
    const items = Array.isArray(event.detail) ? event.detail : [];
    const openIndex = openChipIndexInGroup(groupPath, ctx.openChipPath);
    if (openIndex !== null) {
        const openToken = chipTokenFor(openIndex);
        if (items.every((item) => item.value !== openToken)) {
            ctx.onPopoverClose();
        }
    }
    const newChipNodes = items.flatMap((item) => {
        if (typeof item.value !== 'string') {
            return [];
        }
        const existing = chipsByToken.get(item.value);
        if (existing) {
            return [
                existing.isNegated
                    ? { type: 'not', rule: existing.ref }
                    : existing.ref,
            ];
        }
        return [{ type: 'ref', id: item.value }];
    });
    const nestedGroupRules = groupNode.rules.filter((rule) => extractRef(rule) === null);
    ctx.onReplaceNode(groupPath, Object.assign(Object.assign({}, groupNode), { rules: [...newChipNodes, ...nestedGroupRules] }));
};
const chipInteractHandler = (groupPath, chipsByToken, ctx) => (event) => {
    event.stopPropagation();
    const token = interactedToken(event.detail);
    const chip = token ? chipsByToken.get(token) : undefined;
    if (!chip) {
        return;
    }
    ctx.onChipInteract([...groupPath, 'rules', chip.index]);
};
const chipNegateHandler = (openChip, openChipPath, ctx) => (event) => {
    event.stopPropagation();
    const next = event.detail
        ? { type: 'not', rule: openChip.ref }
        : openChip.ref;
    ctx.onReplaceNode(openChipPath, next);
};
const chipArgsHandler = (openChip, openChipPath, ctx) => (event) => {
    event.stopPropagation();
    const argsPath = openChip.isNegated
        ? [...openChipPath, 'rule']
        : openChipPath;
    ctx.onUpdateArgs(argsPath, event.detail);
};
const chipDeleteHandler = (openChipPath, ctx) => () => {
    ctx.onDelete(openChipPath);
};
/**
 * `limel-picker` declares its `interact` event as
 * `EventEmitter<ListItem<PickerValue>>` but the runtime actually emits
 * the chip's value (a string) — see picker.tsx's handleInteract. This
 * unwraps either shape into the string token.
 * @param detail
 */
function interactedToken(detail) {
    if (typeof detail === 'string') {
        return detail;
    }
    if (detail && typeof detail === 'object' && 'value' in detail) {
        const value = detail.value;
        return typeof value === 'string' ? value : undefined;
    }
    return undefined;
}
function findOpenChip(chipChildren, groupPath, openChipPath) {
    var _a;
    const index = openChipIndexInGroup(groupPath, openChipPath);
    if (index === null) {
        return null;
    }
    return (_a = chipChildren.find((entry) => entry.index === index)) !== null && _a !== void 0 ? _a : null;
}
function openChipIndexInGroup(groupPath, openChipPath) {
    if (!openChipPath || openChipPath.length < CHIP_PATH_SUFFIX_LENGTH) {
        return null;
    }
    if (!pathsEqual(parentGroupPath(openChipPath), groupPath)) {
        return null;
    }
    const lastKey = openChipPath.at(-1);
    return typeof lastKey === 'number' ? lastKey : null;
}

const GroupNodeView = ({ node, path, isNegated, ctx }) => {
    const groupBasePath = isNegated ? [...path, 'rule'] : path;
    const issuesHere = isNegated
        ? [
            ...issuesAt(ctx.issues, path),
            ...issuesAt(ctx.issues, groupBasePath),
        ]
        : issuesAt(ctx.issues, path);
    const chipChildren = [];
    const nestedGroups = [];
    for (const [index, child] of node.rules.entries()) {
        const extracted = extractRef(child);
        if (extracted) {
            chipChildren.push(Object.assign(Object.assign({}, extracted), { index }));
        }
        else {
            nestedGroups.push({ child, index });
        }
    }
    const options = combinatorOptions(ctx);
    const selectedCombinator = options.find((option) => option.value === node.type);
    return (h("div", { class: nodeClasses(node.type, issuesHere), key: pathKey(path) }, h("div", { class: "rule-node__header" }, h("limel-select", { class: "rule-node__combinator-select", value: selectedCombinator, options: options, disabled: !ctx.isMutable, onChange: combinatorChangeHandler(node, path, isNegated, ctx) }), renderMutableGroupActions(node, path, isNegated, ctx)), h("div", { class: "rule-node__body" }, h(IssuesView, { issues: issuesHere }), h(GroupChipsView, { groupNode: node, groupPath: groupBasePath, chipChildren: chipChildren, ctx: ctx }), renderNestedGroups(nestedGroups, groupBasePath, ctx), renderAddGroupFooter(node, path, isNegated, ctx))));
};
function pathKey(path) {
    return path.length === 0 ? 'root' : path.join('/');
}
function combinatorOptions(ctx) {
    return [
        {
            value: 'all',
            text: ctx.translator.get('webclient.rule-editor.combinator.all'),
        },
        {
            value: 'any',
            text: ctx.translator.get('webclient.rule-editor.combinator.any'),
        },
    ];
}
function renderMutableGroupActions(node, path, isNegated, ctx) {
    if (!ctx.isMutable) {
        return null;
    }
    return (h("div", { class: "rule-node__header-actions" }, h("limel-switch", { class: "rule-node__negate-switch", label: ctx.translator.get('webclient.rule-editor.negate'), value: isNegated, onChange: negateGroupHandler(node, path, ctx) }), h("limel-icon-button", { icon: "trash", label: ctx.translator.get('webclient.rule-editor.delete'), onClick: () => ctx.onDelete(path) })));
}
function renderNestedGroups(nestedGroups, groupBasePath, ctx) {
    if (nestedGroups.length === 0) {
        return null;
    }
    return (h("div", { class: "rule-node__children" }, nestedGroups.map((entry) => (h(RuleNodeView, { node: entry.child, path: [...groupBasePath, 'rules', entry.index], ctx: ctx })))));
}
function renderAddGroupFooter(node, path, isNegated, ctx) {
    if (!ctx.isMutable) {
        return null;
    }
    return (h("div", { class: "rule-node__footer" }, h("limel-button", { icon: "add", label: ctx.translator.get('webclient.rule-editor.add-nested-group'), onClick: addGroupHandler(node, path, isNegated, ctx) })));
}
function replaceGroup(nextGroup, path, isNegated, ctx) {
    ctx.onReplaceNode(path, wrapNegated(nextGroup, isNegated));
}
const combinatorChangeHandler = (node, path, isNegated, ctx) => (event) => {
    event.stopPropagation();
    const newType = event.detail.value;
    if (newType !== node.type) {
        replaceGroup(Object.assign(Object.assign({}, node), { type: newType }), path, isNegated, ctx);
    }
};
const addGroupHandler = (node, path, isNegated, ctx) => () => {
    const expanded = appendChild(node, { type: 'all', rules: [] });
    replaceGroup(expanded, path, isNegated, ctx);
};
const negateGroupHandler = (node, path, ctx) => (event) => {
    event.stopPropagation();
    ctx.onReplaceNode(path, wrapNegated(node, event.detail));
};

/**
 * Render a `Rule` node and recurse into its children.
 * @param props
 */
const RuleNodeView = (props) => {
    const { node, path, ctx } = props;
    const group = extractGroup(node);
    if (!group) {
        return null;
    }
    return (h(GroupNodeView, { node: group.node, path: path, isNegated: group.isNegated, ctx: ctx }));
};
const isNegatedGroup = (node) => node.type === 'not' &&
    (node.rule.type === 'all' || node.rule.type === 'any');
/**
 * If `node` is a group (`all`/`any`) or a negated group, return the
 * inner group together with its negation flag. Otherwise `null`.
 * Used by the dispatcher and by the editor's input normalization.
 * @param node
 */
function extractGroup(node) {
    if (node.type === 'all' || node.type === 'any') {
        return { node, isNegated: false };
    }
    if (isNegatedGroup(node)) {
        return { node: node.rule, isNegated: true };
    }
    if (node.type === 'not') {
        const inner = extractGroup(node.rule);
        if (inner) {
            return { node: inner.node, isNegated: !inner.isNegated };
        }
    }
    return null;
}
const IssuesView = ({ issues }) => {
    if (issues.length === 0) {
        return null;
    }
    return issues.map((issue) => (h("div", { class: "rule-node__issue" }, issue.message)));
};

const ruleEditorCss = () => `:host{display:block}.rule-editor--disabled{opacity:0.6;pointer-events:none}.rule-editor__label{font-size:0.75rem;font-weight:600;color:rgb(var(--contrast-1500));margin-bottom:0.25rem}.rule-editor__helper-text{font-size:0.75rem;color:rgb(var(--contrast-1300));margin-top:0.25rem}.rule-node{border:1px solid rgb(var(--contrast-500));border-radius:0.5rem;margin:0.25rem 0;overflow:hidden;background-color:rgb(var(--contrast-100))}.rule-node--invalid{border-color:rgb(var(--color-red-default))}.rule-node--all>.rule-node__header,.rule-node--any>.rule-node__header{background-color:rgb(var(--contrast-300));padding:0.375rem 0.75rem;border-bottom:1px solid rgb(var(--contrast-500))}.rule-node__header{display:flex;align-items:center;gap:0.5rem}.rule-node__header-actions{margin-left:auto;display:grid;grid-auto-flow:column;grid-gap:0.5rem;padding:0.125rem;align-items:center}.rule-node__combinator-select{min-width:6rem}.rule-node__body{padding:0.75rem;display:flex;flex-direction:column;gap:0.75rem}limel-popover.rule-node__chips-popover{display:contents}.rule-node__chips{display:block;width:100%}.rule-node__chips limel-picker{display:block;width:100%}.rule-node__children{display:flex;flex-direction:column;gap:0.5rem}.rule-node__footer{display:flex;justify-content:center}.rule-node__issue{color:rgb(var(--color-red-default));font-size:0.875rem}`;

const RuleEditor = class {
    constructor(hostRef) {
        registerInstance(this, hostRef);
        this.change = createEvent(this, "change");
        this.issues = [];
        this.openChipPath = null;
        this.metadataById = new Map();
        this.primitiveOptions = [];
        this.normalizedCache = null;
        this.handleDelete = (path) => {
            this.openChipPath = null;
            this.emitIfChanged(deleteNode(this.normalizedValue, path));
        };
        this.handleUpdateArgs = (path, args) => {
            this.emitIfChanged(updateArgs(this.normalizedValue, path, args));
        };
        this.handleReplaceNode = (path, replacement) => {
            this.emitIfChanged(replaceNode(this.normalizedValue, path, replacement));
        };
        this.handleChipInteract = (path) => {
            this.openChipPath = path;
        };
        this.handlePopoverClose = () => {
            this.openChipPath = null;
        };
    }
    componentWillLoad() {
        this.indexMetadata();
        this.runValidation();
    }
    componentDidLoad() {
        this.emitNormalization();
    }
    onValueChange() {
        if (this.emitNormalization()) {
            return;
        }
        this.runValidation();
    }
    onAvailableContextsChange() {
        this.runValidation();
    }
    render() {
        const containerClasses = {
            'rule-editor': true,
            'rule-editor--disabled': this.disabled,
        };
        return (h("div", { key: '8e0da39f6544c86de0143e91a6c48c9f2654315f', class: containerClasses }, this.renderLabel(), h(RuleNodeView, { key: 'cd093f5dc5a9b747036f581e6230f1205cf834e6', node: this.normalizedValue, path: [], ctx: this.createRenderContext() }), this.renderHelperText()));
    }
    renderLabel() {
        if (!this.label) {
            return null;
        }
        return h("div", { class: "rule-editor__label" }, this.label);
    }
    renderHelperText() {
        if (!this.helperText) {
            return null;
        }
        return h("div", { class: "rule-editor__helper-text" }, this.helperText);
    }
    createRenderContext() {
        return {
            issues: this.issues,
            metadataById: this.metadataById,
            primitiveOptions: this.primitiveOptions,
            isMutable: !this.readonly && !this.disabled,
            readonly: this.readonly,
            disabled: this.disabled,
            platform: this.platform,
            context: this.context,
            translator: this.platform.get(n.Translate),
            openChipPath: this.openChipPath,
            onDelete: this.handleDelete,
            onUpdateArgs: this.handleUpdateArgs,
            onReplaceNode: this.handleReplaceNode,
            onChipInteract: this.handleChipInteract,
            onPopoverClose: this.handlePopoverClose,
        };
    }
    indexMetadata() {
        var _a, _b, _c;
        const metadata = (_c = (_b = (_a = this.ruleRegistry).listMetadata) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : [];
        this.metadataById = new Map(metadata.map((item) => [item.id, item]));
        this.primitiveOptions = createPrimitiveOptions(this.metadataById, this.platform.get(n.Translate));
    }
    runValidation() {
        if (!this.value) {
            this.issues = [];
            return;
        }
        const result = this.ruleRegistry.validate(this.normalizedValue, this.availableContexts);
        this.issues = result.ok ? [] : result.issues;
    }
    emitIfChanged(next) {
        if (next === this.normalizedValue) {
            return;
        }
        this.change.emit(collapseEmptyRoot(next));
    }
    /**
     * Wrap any non-group `value` in a top-level `all` so the editor
     * always has a group to render against. When `value` is unset,
     * returns a fresh empty `all`-group as a render-only placeholder —
     * `emitNormalization` will *not* push that placeholder back to the
     * caller, so an unset value stays unset until the user edits.
     *
     * Memoized so callers that read this several times in one handler
     * see the same reference — the identity check in `emitIfChanged`
     * depends on it.
     */
    get normalizedValue() {
        if (this.normalizedCache !== null &&
            this.normalizedCache.source === this.value) {
            return this.normalizedCache.normalized;
        }
        const normalized = normalize(this.value);
        this.normalizedCache = { source: this.value, normalized };
        return normalized;
    }
    /**
     * If `value` wasn't already a group, emit the normalized form so
     * the caller's stored state catches up. Skipped when `value` is
     * unset — we render a placeholder group locally but don't push it
     * back to the caller, so callers can't tell "no rule yet" apart
     * from "user cleared a rule" by inspecting the emitted shape.
     * Returns `true` when an emit happened so callers can short-circuit.
     */
    emitNormalization() {
        if (!this.value) {
            return false;
        }
        const normalized = this.normalizedValue;
        if (normalized === this.value) {
            return false;
        }
        this.change.emit(normalized);
        return true;
    }
    get ruleRegistry() {
        return this.platform.get(n.RuleRegistry);
    }
    static get watchers() { return {
        "value": [{
                "onValueChange": 0
            }],
        "availableContexts": [{
                "onAvailableContextsChange": 0
            }]
    }; }
};
function normalize(value) {
    if (!value) {
        return { type: 'all', rules: [] };
    }
    if (extractGroup(value)) {
        return value;
    }
    return { type: 'all', rules: [value] };
}
/**
 * Treat an empty root `all`/`any` as "no rule". Mapping that back to
 * `undefined` on emit keeps the form contract a clean
 * `Rule | undefined` — callers don't have to inspect rule shape to
 * tell "user cleared this" from "user has a real rule". A negated
 * empty group (`not(all())` etc.) is left intact — vacuous either
 * direction, but the user explicitly opted into the wrapper, so we
 * don't second-guess.
 * @param rule
 */
function collapseEmptyRoot(rule) {
    if (rule.type !== 'all' && rule.type !== 'any') {
        return rule;
    }
    return rule.rules.length === 0 ? undefined : rule;
}
RuleEditor.style = ruleEditorCss();

export { RuleEditor as limebb_rule_editor };
