import { h, Host } from "@stencil/core";
import translate from "../../global/translations";
import { buildSplitLines, computeDiff, normalizeForDiff } from "./diff-engine";
import { tokenize } from "./syntax-highlighter";
import { buildSearchRegex, navigateMatchIndex } from "./search-utils";
import { extractRemovedContent, extractRemovedContentFromSplit, } from "./content-utils";
/**
 * Displays a visual diff between two text values, modeled on
 * GitHub's code difference view.
 *
 * Supports unified and split (side-by-side) views with line numbers,
 * color-coded additions and removals, word-level inline highlighting,
 * and collapsible unchanged context sections.
 *
 * @beta
 * @exampleComponent limel-example-code-diff-basic
 * @exampleComponent limel-example-code-diff-headings
 * @exampleComponent limel-example-code-diff-json
 * @exampleComponent limel-example-code-diff-split
 * @exampleComponent limel-example-code-diff-line-wrap
 * @exampleComponent limel-example-code-diff-expand
 */
export class CodeDiff {
    constructor() {
        /**
         * The "before" value to compare.
         * Can be a string or an object (which will be serialized to JSON).
         */
        this.oldValue = '';
        /**
         * The "after" value to compare.
         * Can be a string or an object (which will be serialized to JSON).
         */
        this.newValue = '';
        /**
         * The layout of the diff view.
         * - `unified` — single column with interleaved additions and removals
         * - `split` — side-by-side comparison with old on left, new on right
         */
        this.layout = 'unified';
        /**
         * Number of unchanged context lines to display around each change.
         */
        this.contextLines = 3;
        /**
         * When `true`, long lines are wrapped instead of causing
         * horizontal scrolling. Useful when comparing prose or
         * config files with long values.
         */
        this.lineWrapping = true;
        /**
         * When `true`, JSON values are parsed, keys are sorted,
         * and indentation is normalized before diffing.
         * This eliminates noise from formatting or key-order differences.
         */
        this.reformatJson = false;
        /**
         * Defines the language for translations.
         * Will translate all visible labels and announcements.
         */
        this.translationLanguage = 'en';
        this.diffResult = {
            hunks: [],
            additions: 0,
            deletions: 0,
            allLines: [],
        };
        this.liveAnnouncement = '';
        this.copyState = 'idle';
        this.searchVisible = false;
        this.searchTerm = '';
        this.currentMatchIndex = 0;
        this.focusedRowIndex = -1;
        this.normalizedOldText = '';
        /**
         * Render-time counter that increments for each search match
         * found while rendering removed lines. Used to determine which
         * match is the "current" one for navigation highlighting.
         */
        this.searchMatchCounter = 0;
        /**
         * Total search matches found during the last render pass.
         */
        this.totalSearchMatches = 0;
        /**
         * Whether the current render is inside a removed line,
         * so search highlighting knows when to activate.
         */
        this.isRenderingRemovedLine = false;
        /**
         * Cached search regex for the current render pass.
         * Built once in render() and reused across all renderSearchableText calls.
         */
        this.activeSearchRegex = null;
        this.prevSearchVisible = false;
    }
    componentWillLoad() {
        this.recomputeDiff();
    }
    componentDidRender() {
        var _a, _b;
        if (this.searchVisible && !this.prevSearchVisible) {
            (_a = this.searchInputEl) === null || _a === void 0 ? void 0 : _a.focus();
        }
        this.prevSearchVisible = this.searchVisible;
        if (this.searchTerm && this.totalSearchMatches > 0) {
            const current = (_b = this.host.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('.search-match--current');
            current === null || current === void 0 ? void 0 : current.scrollIntoView({ block: 'center', behavior: 'smooth' });
        }
    }
    render() {
        this.searchMatchCounter = 0;
        this.activeSearchRegex = buildSearchRegex(this.searchTerm);
        const diffContent = this.renderDiff();
        // Capture total matches after rendering completes
        this.totalSearchMatches = this.searchMatchCounter;
        const lineNumberWidth = this.computeLineNumberWidth();
        return (h(Host, { key: '8122ede0d323b9021dae44cd5f65703d03083617', style: { '--limel-line-number-min-width': lineNumberWidth } }, this.renderHeader(), this.renderScreenReaderSummary(), this.searchVisible && this.renderSearchBar(), h("div", { key: 'fefe6e4898cbc82b6eb87f7dff7af71a14c7c91e', class: "diff-body", role: "table", "aria-label": this.getTranslation('code-diff.table-label'), tabindex: "0", onKeyDown: (event) => this.handleKeyDown(event) }, diffContent), h("div", { key: '2af1c7b29f2060a58af13d67fb174cb4de085efb', class: "screen-reader-only", role: "status", "aria-live": "polite", "aria-atomic": "true" }, this.liveAnnouncement)));
    }
    watchInputs() {
        this.recomputeDiff();
    }
    recomputeDiff() {
        const oldText = normalizeForDiff(this.oldValue, this.reformatJson);
        const newText = normalizeForDiff(this.newValue, this.reformatJson);
        this.normalizedOldText = oldText;
        this.diffResult = computeDiff(oldText, newText, this.contextLines);
        this.focusedRowIndex = -1;
    }
    formatSrSummary() {
        const { additions, deletions } = this.diffResult;
        if (additions === 0 && deletions === 0) {
            return null;
        }
        const parts = [];
        if (additions > 0) {
            const key = additions === 1
                ? 'code-diff.diff-addition'
                : 'code-diff.diff-additions';
            parts.push(this.getTranslation(key, { count: additions }));
        }
        if (deletions > 0) {
            const key = deletions === 1
                ? 'code-diff.diff-deletion'
                : 'code-diff.diff-deletions';
            parts.push(this.getTranslation(key, { count: deletions }));
        }
        return this.getTranslation('code-diff.diff-summary', {
            parts: parts.join(', '),
        });
    }
    renderScreenReaderSummary() {
        const summary = this.formatSrSummary();
        return (h("div", { class: "screen-reader-only", role: "status", "aria-live": "polite" }, summary !== null && summary !== void 0 ? summary : this.getTranslation('code-diff.no-differences-found')));
    }
    handleKeyDown(event) {
        if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') {
            return;
        }
        event.preventDefault();
        const rows = this.getDiffRows();
        if (rows.length === 0) {
            return;
        }
        if (event.key === 'ArrowDown') {
            this.focusedRowIndex = Math.min(this.focusedRowIndex + 1, rows.length - 1);
        }
        else {
            this.focusedRowIndex = Math.max(this.focusedRowIndex - 1, 0);
        }
        this.updateRowFocus(rows);
    }
    getDiffRows() {
        var _a;
        const body = (_a = this.host.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.diff-body');
        if (!body) {
            return [];
        }
        return [
            ...body.querySelectorAll('.diff-line:not(.diff-line--collapsed)'),
        ];
    }
    updateRowFocus(rows) {
        for (const row of rows) {
            row.removeAttribute('tabindex');
            row.classList.remove('diff-line--focused');
        }
        const target = rows[this.focusedRowIndex];
        if (target) {
            target.setAttribute('tabindex', '-1');
            target.classList.add('diff-line--focused');
            target.focus();
            this.announceLine(target);
        }
    }
    announceLine(row) {
        var _a, _b;
        let lineType = this.getTranslation('code-diff.line-context');
        if (row.classList.contains('diff-line--added')) {
            lineType = this.getTranslation('code-diff.line-added');
        }
        else if (row.classList.contains('diff-line--removed')) {
            lineType = this.getTranslation('code-diff.line-removed');
        }
        const content = (_b = (_a = row.querySelector('.line-content, .split-content')) === null || _a === void 0 ? void 0 : _a.textContent) !== null && _b !== void 0 ? _b : '';
        const trimmed = content.length > 80 ? content.slice(0, 80) + '…' : content;
        this.liveAnnouncement = `${lineType}: ${trimmed}`;
    }
    renderHeader() {
        var _a, _b;
        const oldHeading = (_a = this.oldHeading) !== null && _a !== void 0 ? _a : this.getTranslation('code-diff.old-heading');
        const newHeading = (_b = this.newHeading) !== null && _b !== void 0 ? _b : this.getTranslation('code-diff.new-heading');
        const { additions, deletions } = this.diffResult;
        const hasDiff = additions > 0 || deletions > 0;
        return (h("div", { class: "diff-header" }, h("div", { class: "diff-header__labels" }, h("span", { class: "diff-header__old" }, oldHeading), h("span", { class: "diff-header__new" }, newHeading)), h("div", { class: "diff-header__actions" }, h("div", { class: "diff-header__stats" }, additions > 0 && (h("span", { class: "stat stat--added" }, "+", additions)), deletions > 0 && (h("span", { class: "stat stat--removed" }, "-", deletions))), hasDiff && this.renderCopyButton(), deletions > 0 && this.renderSearchToggle())));
    }
    renderCopyButton() {
        const label = this.copyState === 'copied'
            ? this.getTranslation('code-diff.copied')
            : this.getTranslation('code-diff.copy-old-version');
        const icon = this.copyState === 'copied' ? 'checkmark' : 'copy';
        return (h("limel-icon-button", { label: label, icon: icon, onClick: () => this.copyToClipboard(this.normalizedOldText) }));
    }
    async copyToClipboard(text) {
        try {
            await navigator.clipboard.writeText(text);
            this.copyState = 'copied';
            this.liveAnnouncement = this.getTranslation('code-diff.copied-to-clipboard');
            setTimeout(() => {
                this.copyState = 'idle';
            }, 2000);
        }
        catch (_a) {
            // Clipboard API may fail in insecure contexts
        }
    }
    renderSearchToggle() {
        return (h("limel-icon-button", { class: { 'search-toggle--active': this.searchVisible }, label: this.getTranslation('code-diff.search'), icon: "search", onClick: () => this.toggleSearch() }));
    }
    renderSearchBar() {
        const matchInfo = this.totalSearchMatches === 0
            ? this.getTranslation('code-diff.no-matches')
            : this.getTranslation('code-diff.match-count', {
                current: this.currentMatchIndex + 1,
                total: this.totalSearchMatches,
            });
        return (h("div", { class: "search-bar" }, h("limel-input-field", { class: "search-bar__input", type: "search", placeholder: this.getTranslation('code-diff.search') + '…', value: this.searchTerm, onChange: (e) => this.onSearchInput(e), onKeyDown: (e) => this.onSearchKeyDown(e), ref: (el) => (this.searchInputEl = el) }), h("span", { class: "search-bar__count" }, matchInfo), h("limel-action-bar", { actions: this.getSearchActions(), onItemSelected: (e) => this.onSearchAction(e) })));
    }
    toggleSearch() {
        this.searchVisible = !this.searchVisible;
        if (!this.searchVisible) {
            this.searchTerm = '';
            this.currentMatchIndex = 0;
        }
    }
    onSearchInput(event) {
        this.searchTerm = event.detail;
        this.currentMatchIndex = 0;
    }
    onSearchKeyDown(event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            if (event.shiftKey) {
                this.navigateSearch(-1);
            }
            else {
                this.navigateSearch(1);
            }
        }
        else if (event.key === 'Escape') {
            this.toggleSearch();
        }
    }
    computeLineNumberWidth() {
        const maxLineNumber = this.diffResult.allLines.length;
        const digits = String(maxLineNumber).length;
        return `calc(${digits}ch + 2 * var(--limel-code-diff-line-number-padding))`;
    }
    getSearchActions() {
        const noMatches = this.totalSearchMatches === 0;
        return [
            {
                text: this.getTranslation('code-diff.previous-match'),
                icon: '-lime-caret-top',
                iconOnly: true,
                disabled: noMatches,
                value: 'prev',
            },
            {
                text: this.getTranslation('code-diff.next-match'),
                icon: '-lime-caret-bottom',
                iconOnly: true,
                disabled: noMatches,
                value: 'next',
            },
            {
                text: this.getTranslation('code-diff.close-search'),
                icon: 'cancel',
                iconOnly: true,
                value: 'close',
            },
        ];
    }
    onSearchAction(event) {
        const { value } = event.detail;
        if (value === 'prev') {
            this.navigateSearch(-1);
        }
        else if (value === 'next') {
            this.navigateSearch(1);
        }
        else if (value === 'close') {
            this.toggleSearch();
        }
    }
    navigateSearch(direction) {
        this.currentMatchIndex = navigateMatchIndex(this.currentMatchIndex, direction, this.totalSearchMatches);
    }
    renderDiff() {
        const { hunks, collapsedAfter } = this.diffResult;
        if (hunks.length === 0) {
            return (h("div", { class: "diff-empty" }, this.getTranslation('code-diff.no-differences')));
        }
        const lineRenderer = this.layout === 'split'
            ? (hunk) => this.renderSplitHunkRows(hunk)
            : (hunk) => this.renderHunkLines(hunk);
        return this.renderHunks(hunks, collapsedAfter, lineRenderer);
    }
    renderHunks(hunks, collapsedAfter, lineRenderer) {
        const elements = [];
        for (const [i, hunk] of hunks.entries()) {
            if (hunk.collapsedBefore) {
                elements.push(this.renderCollapsedRow(hunk.collapsedBefore, i));
            }
            elements.push(...lineRenderer(hunk));
        }
        if (collapsedAfter) {
            elements.push(this.renderCollapsedAfterRow(collapsedAfter));
        }
        return elements;
    }
    renderHunkLines(hunk) {
        const elements = [];
        let i = 0;
        while (i < hunk.lines.length) {
            const line = hunk.lines[i];
            if (line.type === 'context') {
                elements.push(this.renderLine(line));
                i++;
                continue;
            }
            // Collect consecutive changed lines as a change block
            const blockLines = [];
            while (i < hunk.lines.length && hunk.lines[i].type !== 'context') {
                blockLines.push(hunk.lines[i]);
                i++;
            }
            elements.push(this.renderChangeBlock(blockLines));
        }
        return elements;
    }
    renderChangeBlock(lines) {
        const removedContent = extractRemovedContent(lines);
        return (h("div", { class: "change-group" }, lines.map((line) => this.renderLine(line)), removedContent && this.renderBlockCopyButton(removedContent)));
    }
    renderLine(line) {
        var _a, _b;
        const lineClass = {
            'diff-line': true,
            [`diff-line--${line.type}`]: true,
        };
        const indicatorMap = {
            added: '+',
            removed: '-',
            context: ' ',
        };
        const indicator = indicatorMap[line.type];
        return (h("div", { class: lineClass, role: "row" }, h("span", { class: "line-number line-number--old", role: "cell", "aria-label": line.oldLineNumber
                ? this.getTranslation('code-diff.old-line', {
                    number: line.oldLineNumber,
                })
                : undefined }, (_a = line.oldLineNumber) !== null && _a !== void 0 ? _a : ''), h("span", { class: "line-number line-number--new", role: "cell", "aria-label": line.newLineNumber
                ? this.getTranslation('code-diff.new-line', {
                    number: line.newLineNumber,
                })
                : undefined }, (_b = line.newLineNumber) !== null && _b !== void 0 ? _b : ''), h("span", { class: "line-indicator", role: "cell" }, indicator), h("span", { class: "line-content", role: "cell" }, this.renderContent(line))));
    }
    renderSplitHunkRows(hunk) {
        var _a, _b, _c, _d;
        const splitRows = buildSplitLines(hunk.lines);
        const elements = [];
        let i = 0;
        while (i < splitRows.length) {
            const row = splitRows[i];
            const isContext = ((_a = row.left) === null || _a === void 0 ? void 0 : _a.type) === 'context' && ((_b = row.right) === null || _b === void 0 ? void 0 : _b.type) === 'context';
            if (isContext) {
                elements.push(this.renderSplitRow(row));
                i++;
                continue;
            }
            // Collect consecutive changed rows
            const blockRows = [];
            while (i < splitRows.length) {
                const r = splitRows[i];
                const rIsContext = ((_c = r.left) === null || _c === void 0 ? void 0 : _c.type) === 'context' && ((_d = r.right) === null || _d === void 0 ? void 0 : _d.type) === 'context';
                if (rIsContext) {
                    break;
                }
                blockRows.push(r);
                i++;
            }
            elements.push(this.renderSplitChangeBlock(blockRows));
        }
        return elements;
    }
    renderSplitChangeBlock(rows) {
        const removedContent = extractRemovedContentFromSplit(rows);
        return (h("div", { class: "change-group" }, rows.map((row) => this.renderSplitRow(row)), removedContent && this.renderBlockCopyButton(removedContent)));
    }
    renderSplitRow(row) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        const leftType = (_b = (_a = row.left) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : 'empty';
        const rightType = (_d = (_c = row.right) === null || _c === void 0 ? void 0 : _c.type) !== null && _d !== void 0 ? _d : 'empty';
        const oldLineLabel = ((_e = row.left) === null || _e === void 0 ? void 0 : _e.oldLineNumber)
            ? this.getTranslation('code-diff.old-line', {
                number: row.left.oldLineNumber,
            })
            : undefined;
        const newLineLabel = ((_f = row.right) === null || _f === void 0 ? void 0 : _f.newLineNumber)
            ? this.getTranslation('code-diff.new-line', {
                number: row.right.newLineNumber,
            })
            : undefined;
        return (h("div", { class: "diff-line diff-line--split", role: "row" }, h("span", { class: "line-number line-number--old", role: "cell", "aria-label": oldLineLabel }, (_h = (_g = row.left) === null || _g === void 0 ? void 0 : _g.oldLineNumber) !== null && _h !== void 0 ? _h : ''), h("span", { class: `split-content split-content--left split-content--${leftType}`, role: "cell" }, row.left ? this.renderContent(row.left) : ''), h("span", { class: "line-number line-number--new", role: "cell", "aria-label": newLineLabel }, (_k = (_j = row.right) === null || _j === void 0 ? void 0 : _j.newLineNumber) !== null && _k !== void 0 ? _k : ''), h("span", { class: `split-content split-content--right split-content--${rightType}`, role: "cell" }, row.right ? this.renderContent(row.right) : '')));
    }
    renderBlockCopyButton(removedContent) {
        return (h("limel-icon-button", { class: "change-group__copy", elevated: true, label: this.getTranslation('code-diff.copy-change'), icon: "copy", onClick: () => this.copyToClipboard(removedContent) }));
    }
    renderContent(line) {
        this.isRenderingRemovedLine =
            line.type === 'removed' && this.searchTerm.length > 0;
        if (!line.segments || line.segments.length === 0) {
            return this.renderSyntaxTokens(line.content);
        }
        return line.segments.map((segment) => this.renderSegment(segment, line.type));
    }
    renderSegment(segment, lineType) {
        const content = this.renderSyntaxTokens(segment.value);
        if (segment.type === 'equal') {
            return content;
        }
        const segmentClass = lineType === 'removed' ? 'segment--removed' : 'segment--added';
        return h("mark", { class: segmentClass }, content);
    }
    renderSyntaxTokens(text) {
        const tokens = tokenize(text, this.language);
        if (tokens.length === 1 && tokens[0].type === 'plain') {
            return this.renderSearchableText(text);
        }
        return tokens.map((token) => this.renderSyntaxToken(token));
    }
    renderSyntaxToken(token) {
        const text = this.renderSearchableText(token.value);
        if (token.type === 'plain') {
            return text;
        }
        return h("span", { class: `syntax--${token.type}` }, text);
    }
    renderSearchableText(text) {
        if (!this.isRenderingRemovedLine || !this.activeSearchRegex) {
            return text;
        }
        const parts = text.split(this.activeSearchRegex);
        if (parts.length === 1) {
            return text;
        }
        return parts.map((part, i) => {
            // Odd indices are the captured matches from split
            if (i % 2 === 0) {
                return part;
            }
            const matchIndex = this.searchMatchCounter++;
            const isCurrent = matchIndex === this.currentMatchIndex;
            const cls = {
                'search-match': true,
                'search-match--current': isCurrent,
            };
            return (h("mark", { key: `match-${matchIndex}`, class: cls }, part));
        });
    }
    renderCollapsedRow(count, hunkIndex) {
        return (h("div", { class: "diff-line diff-line--collapsed", role: "row" }, h("button", { class: "expand-button", type: "button", onClick: () => this.expandHunk(hunkIndex), "aria-label": this.getTranslation('code-diff.show-hidden-lines', {
                count,
            }) }, this.getTranslation('code-diff.hidden-lines', { count }))));
    }
    renderCollapsedAfterRow(count) {
        return (h("div", { class: "diff-line diff-line--collapsed", role: "row" }, h("button", { class: "expand-button", type: "button", onClick: () => this.expandAfter(), "aria-label": this.getTranslation('code-diff.show-hidden-lines', {
                count,
            }) }, this.getTranslation('code-diff.hidden-lines', { count }))));
    }
    expandHunk(hunkIndex) {
        const hunks = [...this.diffResult.hunks];
        const hunk = hunks[hunkIndex];
        const prevHunkEnd = hunkIndex > 0
            ? hunks[hunkIndex - 1].startIndex +
                hunks[hunkIndex - 1].lines.length
            : 0;
        const hiddenLines = this.diffResult.allLines.slice(prevHunkEnd, hunk.startIndex);
        hunks[hunkIndex] = Object.assign(Object.assign({}, hunk), { lines: [...hiddenLines, ...hunk.lines], collapsedBefore: undefined, startIndex: prevHunkEnd });
        this.diffResult = Object.assign(Object.assign({}, this.diffResult), { hunks });
        this.liveAnnouncement = this.getTranslation('code-diff.expanded-lines');
    }
    expandAfter() {
        const hunks = [...this.diffResult.hunks];
        const lastIndex = hunks.length - 1;
        const lastHunk = hunks[lastIndex];
        const lastHunkEnd = lastHunk.startIndex + lastHunk.lines.length;
        const hiddenLines = this.diffResult.allLines.slice(lastHunkEnd);
        hunks[lastIndex] = Object.assign(Object.assign({}, lastHunk), { lines: [...lastHunk.lines, ...hiddenLines] });
        this.diffResult = Object.assign(Object.assign({}, this.diffResult), { hunks, collapsedAfter: undefined });
        this.liveAnnouncement = this.getTranslation('code-diff.expanded-lines-end');
    }
    getTranslation(key, params) {
        return translate.get(key, this.translationLanguage, params);
    }
    static get is() { return "limel-code-diff"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["code-diff.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["code-diff.css"]
        };
    }
    static get properties() {
        return {
            "oldValue": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string | object",
                    "resolved": "object | string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The \"before\" value to compare.\nCan be a string or an object (which will be serialized to JSON)."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "old-value",
                "defaultValue": "''"
            },
            "newValue": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string | object",
                    "resolved": "object | string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The \"after\" value to compare.\nCan be a string or an object (which will be serialized to JSON)."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "new-value",
                "defaultValue": "''"
            },
            "oldHeading": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Heading for the original (before) version, displayed in the diff header.\nDefaults to `\"Original\"`, localized via `translationLanguage`."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "old-heading"
            },
            "newHeading": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Heading for the modified (after) version, displayed in the diff header.\nDefaults to `\"Modified\"`, localized via `translationLanguage`."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "new-heading"
            },
            "layout": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "'unified' | 'split'",
                    "resolved": "\"split\" | \"unified\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The layout of the diff view.\n- `unified` \u2014 single column with interleaved additions and removals\n- `split` \u2014 side-by-side comparison with old on left, new on right"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "layout",
                "defaultValue": "'unified'"
            },
            "contextLines": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Number of unchanged context lines to display around each change."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "context-lines",
                "defaultValue": "3"
            },
            "lineWrapping": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "When `true`, long lines are wrapped instead of causing\nhorizontal scrolling. Useful when comparing prose or\nconfig files with long values."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "line-wrapping",
                "defaultValue": "true"
            },
            "language": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "Language for syntax highlighting.\nCurrently supports `\"json\"`. When set, code tokens are\ncolorized (strings, numbers, keys, etc.) alongside the\ndiff highlighting."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "language"
            },
            "reformatJson": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "When `true`, JSON values are parsed, keys are sorted,\nand indentation is normalized before diffing.\nThis eliminates noise from formatting or key-order differences."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "reformat-json",
                "defaultValue": "false"
            },
            "translationLanguage": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "Languages",
                    "resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"",
                    "references": {
                        "Languages": {
                            "location": "import",
                            "path": "../date-picker/date.types",
                            "id": "src/components/date-picker/date.types.ts::Languages",
                            "referenceLocation": "Languages"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Defines the language for translations.\nWill translate all visible labels and announcements."
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "translation-language",
                "defaultValue": "'en'"
            }
        };
    }
    static get states() {
        return {
            "diffResult": {},
            "liveAnnouncement": {},
            "copyState": {},
            "searchVisible": {},
            "searchTerm": {},
            "currentMatchIndex": {}
        };
    }
    static get elementRef() { return "host"; }
    static get watchers() {
        return [{
                "propName": "oldValue",
                "methodName": "watchInputs"
            }, {
                "propName": "newValue",
                "methodName": "watchInputs"
            }, {
                "propName": "contextLines",
                "methodName": "watchInputs"
            }, {
                "propName": "reformatJson",
                "methodName": "watchInputs"
            }, {
                "propName": "layout",
                "methodName": "watchInputs"
            }];
    }
}
