import { NgIf, NgClass, JsonPipe, AsyncPipe, NgFor } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, Injectable, Input, Component, viewChild, SecurityContext, ViewChild } from '@angular/core';
import * as i2 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { gettext } from '@c8y/ngx-components/gettext';
import * as i2$1 from '@c8y/ngx-components';
import { AppStateService, Permissions, IconDirective, C8yTranslatePipe, ListGroupComponent, ListItemComponent, ListItemCollapseComponent, ListItemBodyComponent, ListItemIconComponent, MarkdownToHtmlPipe, TabsModule, LoadingComponent, OptionsService, ClipboardService } from '@c8y/ngx-components';
import { WidgetConfigService, WidgetConfigFeedbackComponent } from '@c8y/ngx-components/context-dashboard';
import * as i1 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import * as i3 from 'ngx-bootstrap/tooltip';
import { TooltipModule, TooltipDirective } from 'ngx-bootstrap/tooltip';
import { isEmpty } from 'lodash';
import { Subject, first, map, filter, withLatestFrom, switchMap, shareReplay, takeUntil, startWith, combineLatest, distinctUntilChanged, debounceTime, of, merge, from, isEmpty as isEmpty$1, catchError, EMPTY, fromEvent } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';
import { InventoryService } from '@c8y/client';
import { kebabCase } from 'lodash-es';
import { EditorComponent } from '@c8y/ngx-components/editor';
import { AssetPropertyListComponent, AssetPropertyActionDirective } from '@c8y/ngx-components/asset-properties';

const INITIAL_HTML_FORMATTED = `<div>
  <h2>Hello from <span class="branded">HTML widget</span></h2>
  <p class="m-b-8 m-t-16">
  You can use HTML and Javascript template literals here: <br>
  \$\{this.c8yContext ? this.c8yContext.name : 'No device selected'\}
  </p>

  <a class="btn btn-primary m-b-16" href="#/group">Go to groups</a>

  <p>
    Use the CSS editor to customize the CSS. You can use <span class="text-bold">any design-token CSS variable</span> in there.
  </p>
</div>`;
const INITIAL_CSS_FORMATTED = `
:host > div {
  padding: var(--c8y-root-component-padding-default);
}
span.branded { 
  color: var(--brand-primary, var(--c8y-brand-primary)); 
}`;
const defaultWebComponentName = 'DefaultWebComponent';
const defaultWebComponentAttributeNameContext = 'c8yContext';

const webComponentTemplate = (html, css, viewEncapsulation, name = defaultWebComponentName) => `
import { LitElement, html, css} from 'lit';
${!viewEncapsulation ? `import { styleImports } from 'styles';` : ''}

export default class ${name} extends LitElement {
  static styles = css\`
    ${css}
  \`;

  static properties = {
    // The managed object this widget is assigned to. Can be null.
    c8yContext: { type: Object },
  };

  constructor() {
    super();
  }

  render() {
    return html\`${viewEncapsulation
    ? html
    : `
      <style>
        \${styleImports}
      </style>
      ${html}
    `}\`;
  }
}
`;

const legacyTemplate = (html, deviceId, deviceName) => {
    const escapedHtml = html
        // Finds: \  Replaces with: \\
        .replace(/\\/g, '\\\\')
        // Finds: ` Replaces with: \`
        .replace(/`/g, '\\`')
        // Finds: $ Replaces with: \$
        .replace(/\$/g, '\\$');
    return `
import { angular } from 'angular';

// NOTE: This is a legacy template for the HTML widget.
// It is used to compile the HTML content in the context of the AngularJS application.
// The template is injected into the AngularJS application and compiled using the AngularJS compiler.
// The template should only be used for backward compatibility purposes.
// It is recommended to use a web component instead.

if(!angular) {
  throw new Error('AngularJS is not available. Please make sure to include AngularJS in your project.');
}

const $injector = angular.element(document.querySelector('c8y-ui-root')).injector();
if (!$injector) {
  throw new Error('AngularJS injector is not available. Maybe not an hybrid application?');
}

// defining a new scope
const $rootScope = $injector.get('$rootScope');
const $scope = $rootScope.$new(true); 

// faking the old angularjs config 
$scope.child = { 
  config: {
    ${deviceId ? `device: { id: "${deviceId}", name: "${deviceName}" },` : ''}
    html: \`<div ng-controller="HtmlWidgetCtrl">${escapedHtml}</div>\`
  }
};

// load the needed services
const $compile = $injector.get('$compile');
const $controller = $injector.get('$controller');

// create the element
const htmlElement = angular.element($scope.child.config.html);

// The default controller providing the context
$controller('HtmlWidgetCtrl', { $scope });

// Compile the element
$compile(htmlElement)($scope);

// Apply the scope changes
$rootScope.$apply();

export default htmlElement[0];`;
};

class HtmlWidgetConfigService {
    constructor() {
        this.DEFAULT_AUTO_SAVE_DEBOUNCE = 1000;
        this.codeChange$ = new Subject();
        this.widgetConfigService = inject(WidgetConfigService);
        this.appState = inject(AppStateService);
        this.destroy$ = new Subject();
        this.init$ = this.widgetConfigService.currentConfig$.pipe(first(), map(current => {
            if (current.html) {
                current.config = this.mapLegacyConfig(current);
            }
            return (current.config || {});
        }), filter(config => !!config), withLatestFrom(this.appState.currentApplicationConfig), switchMap(([widgetConfig, appConfig]) => this.initConfig(appConfig, widgetConfig)), shareReplay(), takeUntil(this.destroy$));
        this.config$ = this.init$.pipe(switchMap(initValue => this.configChanged$.pipe(startWith(initValue))));
        this.codeEditorChangeConfig$ = combineLatest([
            this.codeChange$.pipe(startWith(undefined)),
            this.config$
        ]).pipe(distinctUntilChanged(), takeUntil(this.destroy$), debounceTime(this.DEFAULT_AUTO_SAVE_DEBOUNCE), map(([change, config]) => {
            if (!change) {
                return config;
            }
            if (change.type === 'css') {
                config.css = change.value;
            }
            else {
                config.code = change.value;
            }
            return { ...config };
        }));
        this.configChanged$ = new Subject();
    }
    initConfig(appConfig, widgetConfig) {
        const defaultToAdvancedMode = appConfig?.htmlWidgetDefaultToAdvancedMode ?? false;
        const isEmptyConfig = isEmpty(widgetConfig);
        if (isEmptyConfig && !defaultToAdvancedMode) {
            widgetConfig = this.initDefaultMode(!appConfig?.htmlWidgetDisableSanitization);
            this.save(widgetConfig);
            return of(widgetConfig);
        }
        if (isEmptyConfig) {
            widgetConfig = this.enableAdvancedMode(widgetConfig);
        }
        // new config is needed to trigger ngOnChanges
        const newConfig = { ...widgetConfig };
        this.save(newConfig);
        return of(newConfig);
    }
    destroy() {
        this.destroy$.next();
        this.codeChange$.complete();
        this.configChanged$.complete();
    }
    save(config) {
        this.widgetConfigService.updateConfig({
            config
        });
    }
    changeCode(value) {
        this.codeChange$.next({ value, type: 'code' });
    }
    changeCss(value) {
        this.codeChange$.next({ value, type: 'css' });
    }
    enableAdvancedMode(currentConfig) {
        const currentHTML = currentConfig?.code || INITIAL_HTML_FORMATTED;
        const currentCSS = currentConfig?.css || INITIAL_CSS_FORMATTED;
        const code = currentConfig?.legacy
            ? legacyTemplate(currentHTML, this.widgetConfigService.currentConfig?.device?.id, this.widgetConfigService.currentConfig?.device?.name)
            : webComponentTemplate(currentHTML, currentCSS, false);
        currentConfig = {
            css: '',
            code,
            legacy: false,
            devMode: true,
            options: {
                cssEncapsulation: false,
                advancedSecurity: false
            }
        };
        return currentConfig;
    }
    initDefaultMode(advancedSecurity = true) {
        return {
            css: INITIAL_CSS_FORMATTED,
            code: INITIAL_HTML_FORMATTED,
            legacy: false,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity
            }
        };
    }
    mapLegacyConfig(current) {
        const isAlreadyInAdvancedMode = current?.config?.devMode === true;
        if (isAlreadyInAdvancedMode) {
            return current.config;
        }
        const isAlreadyMapped = current?.config?.legacy === true;
        if (isAlreadyMapped) {
            return current.config;
        }
        return {
            code: current.html,
            css: '',
            legacy: true,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity: current.sanitization === 'strict'
            }
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetConfigService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetConfigService, decorators: [{
            type: Injectable
        }] });

class AdvancedSettingsComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
        this.router = inject(Router);
        this.permissionService = inject(Permissions);
        this.canChangeSettings = false;
        this.CSS_ENCAPSULATION_HELP_CONTEXT = gettext('If enabled, the CSS will be encapsulated and no platform styling will be applied.');
    }
    ngOnInit() {
        this.canChangeSettings = this.permissionService.hasAnyRole([
            Permissions.ROLE_APPLICATION_MANAGEMENT_ADMIN,
            Permissions.ROLE_TENANT_ADMIN
        ]);
    }
    disableAdvancedMode() {
        const config = this.htmlWidgetConfigService.initDefaultMode();
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    enableAdvancedMode() {
        let { config } = this.widgetConfigService.currentConfig;
        config = this.htmlWidgetConfigService.enableAdvancedMode(config);
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    toggleAdvancedMode() {
        this.devMode ? this.disableAdvancedMode() : this.enableAdvancedMode();
        this.devMode = !this.devMode;
    }
    async changeOption(option) {
        const { config } = this.widgetConfigService.currentConfig;
        config.options[option] = !config.options[option];
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AdvancedSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: AdvancedSettingsComponent, isStandalone: true, selector: "c8y-html-widget-advanced-settings", inputs: { devMode: "devMode", cssEncapsulation: "cssEncapsulation" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset m-t-0\">\n  <legend>{{ 'Developer mode' | translate }}</legend>\n\n  <div class=\"d-flex a-i-center p-b-16\">\n    <label class=\"c8y-switch\">\n      <input\n        type=\"checkbox\"\n        [ngModel]=\"devMode\"\n        (change)=\"toggleAdvancedMode()\"\n        [disabled]=\"!canChangeSettings\"\n      />\n      <span></span>\n      <span>{{ 'Advanced developer mode' | translate }}</span>\n    </label>\n\n    <button class=\"btn-help\"\n      [attr.aria-label]=\"'Help' | translate\"\n      [popover]=\"devMode ? disableAdvanced : enableAdvanced\"\n      container=\"body\"\n      placement=\"right\"\n      triggers=\"focus\"\n      type=\"button\"\n    ></button>\n\n    <ng-template #enableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        <i [c8yIcon]=\"'imac-settings'\"></i>\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        Create custom widgets by modifying a basic WebComponent with HTML and JavaScript. This\n        <strong>unsupported</strong>\n        feature is ideal for rapid prototyping and simple customizations.\n      </p>\n      <p class=\"p-b-8\" translate>\n        For production environments, we recommend our fully-supported Angular-based\n        <a href=\"https://styleguide.cumulocity.com\" target=\"_blank\">Web SDK</a>.\n        <br />\n        Enable advanced developer mode to start coding!\n      </p>\n    </ng-template>\n\n    <ng-template #disableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        The advanced developer mode is enabled for this widget allowing to build extensive Web\n        Components.\n      </p>\n      <p class=\"p-b-8\" translate>\n        You can disable this mode again, but it will reset the current code.\n      </p>\n    </ng-template>\n\n    <ng-container *ngIf=\"!devMode\">\n      <label\n        class=\"c8y-switch m-l-auto\"\n      >\n        <input\n          type=\"checkbox\"\n          (change)=\"changeOption('cssEncapsulation')\"\n          [disabled]=\"!canChangeSettings\"\n          [ngModel]=\"cssEncapsulation\"\n        />\n        <span></span>\n        <span>{{ 'CSS encapsulation' | translate }}</span>\n      </label>\n      <button\n        class=\"btn-help m-0\"\n        [attr.aria-label]=\"'Help' | translate\"\n        popover=\"{{ CSS_ENCAPSULATION_HELP_CONTEXT | translate }}\"\n        triggers=\"focus\"\n        placement=\"left\"\n        container=\"body\"\n        type=\"button\"\n      ></button>\n    </ng-container>\n\n  </div>\n</fieldset>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AdvancedSettingsComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [IconDirective, NgIf, TooltipModule, PopoverModule, C8yTranslatePipe, FormsModule], selector: 'c8y-html-widget-advanced-settings', template: "<fieldset class=\"c8y-fieldset m-t-0\">\n  <legend>{{ 'Developer mode' | translate }}</legend>\n\n  <div class=\"d-flex a-i-center p-b-16\">\n    <label class=\"c8y-switch\">\n      <input\n        type=\"checkbox\"\n        [ngModel]=\"devMode\"\n        (change)=\"toggleAdvancedMode()\"\n        [disabled]=\"!canChangeSettings\"\n      />\n      <span></span>\n      <span>{{ 'Advanced developer mode' | translate }}</span>\n    </label>\n\n    <button class=\"btn-help\"\n      [attr.aria-label]=\"'Help' | translate\"\n      [popover]=\"devMode ? disableAdvanced : enableAdvanced\"\n      container=\"body\"\n      placement=\"right\"\n      triggers=\"focus\"\n      type=\"button\"\n    ></button>\n\n    <ng-template #enableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        <i [c8yIcon]=\"'imac-settings'\"></i>\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        Create custom widgets by modifying a basic WebComponent with HTML and JavaScript. This\n        <strong>unsupported</strong>\n        feature is ideal for rapid prototyping and simple customizations.\n      </p>\n      <p class=\"p-b-8\" translate>\n        For production environments, we recommend our fully-supported Angular-based\n        <a href=\"https://styleguide.cumulocity.com\" target=\"_blank\">Web SDK</a>.\n        <br />\n        Enable advanced developer mode to start coding!\n      </p>\n    </ng-template>\n\n    <ng-template #disableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        The advanced developer mode is enabled for this widget allowing to build extensive Web\n        Components.\n      </p>\n      <p class=\"p-b-8\" translate>\n        You can disable this mode again, but it will reset the current code.\n      </p>\n    </ng-template>\n\n    <ng-container *ngIf=\"!devMode\">\n      <label\n        class=\"c8y-switch m-l-auto\"\n      >\n        <input\n          type=\"checkbox\"\n          (change)=\"changeOption('cssEncapsulation')\"\n          [disabled]=\"!canChangeSettings\"\n          [ngModel]=\"cssEncapsulation\"\n        />\n        <span></span>\n        <span>{{ 'CSS encapsulation' | translate }}</span>\n      </label>\n      <button\n        class=\"btn-help m-0\"\n        [attr.aria-label]=\"'Help' | translate\"\n        popover=\"{{ CSS_ENCAPSULATION_HELP_CONTEXT | translate }}\"\n        triggers=\"focus\"\n        placement=\"left\"\n        container=\"body\"\n        type=\"button\"\n      ></button>\n    </ng-container>\n\n  </div>\n</fieldset>\n" }]
        }], propDecorators: { devMode: [{
                type: Input
            }], cssEncapsulation: [{
                type: Input
            }] } });

class HtmlAiChatFeedbackComponent {
    constructor() {
        /**
         * Indicates whether the feedback section is in a loading state.
         */
        this.loading = false;
        /**
         * Indicates whether the detailed feedback section is collapsed.
         */
        this.collapsed = true;
        /**
         * Indicates whether the feedback section can be collapsed.
         */
        this.canCollapse = false;
        /**
         * The code extracted from the agent step (if any).
         */
        this.code = '';
        /**
         * The text before the code block (if any).
         */
        this.textBeforeCode = '';
        /**
         * The text after the code block (if any).
         */
        this.textAfterCode = '';
        this.codeTag = 'c8y-code-extract';
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
    }
    /**
     * @ignore
     */
    ngOnInit() {
        if (this.step) {
            this.parseAgentStep(this.step);
        }
    }
    /**
     * @ignore
     */
    ngOnChanges(changes) {
        if (changes.step) {
            this.parseAgentStep(changes.step.currentValue);
        }
    }
    /**
     * Parse the agent step. Extracts the code block if present and updates the state accordingly.
     * @param step The agent step to parse.
     */
    parseAgentStep(step) {
        if (step.reasoning) {
            this.label = gettext('Reasoning');
            this.loading = false;
            this.canCollapse = true;
            this.collapsed = false;
        }
        if (step.toolCalls?.length > 0 && !step.toolResults?.length) {
            this.label = gettext('Analyzing query…');
            this.loading = true;
            this.canCollapse = false;
            this.collapsed = true;
        }
        else if (step.toolResults?.length > 0) {
            this.label = gettext('Query analyzed.');
            this.loading = false;
            this.canCollapse = true;
            this.collapsed = true;
        }
        this.parseCodeBlock(step);
    }
    /**
     * Revert to the last applied code.
     */
    revert() {
        this.applyCurrentCode();
    }
    parseCodeBlock(step) {
        const text = step.text;
        const codeBlockStart = text.lastIndexOf(`<${this.codeTag}>`);
        const codeBlockEnd = text.lastIndexOf(`</${this.codeTag}>`);
        const codeBlockStartLength = this.codeTag.length + 2;
        const codeBlockEndLength = this.codeTag.length + 3;
        if (codeBlockStart !== -1) {
            this.code = text.substring(codeBlockStart + codeBlockStartLength);
            this.textBeforeCode = text.substring(0, codeBlockStart + codeBlockStartLength);
            this.label = gettext('Creating widget…');
            this.loading = true;
            this.canCollapse = true;
            this.collapsed = true;
        }
        if (codeBlockEnd !== -1) {
            this.label = gettext('Widget created');
            this.code = text.substring(codeBlockStart + codeBlockStartLength, codeBlockEnd);
            this.loading = false;
            this.textAfterCode = text.substring(codeBlockEnd + codeBlockEndLength);
            this.canCollapse = true;
            this.collapsed = true;
            this.applyCurrentCode();
        }
    }
    applyCurrentCode() {
        const newConfig = {
            code: this.code,
            css: '',
            devMode: true,
            legacy: false,
            options: { advancedSecurity: false, cssEncapsulation: false }
        };
        this.htmlWidgetConfigService.configChanged$.next(newConfig);
        this.htmlWidgetConfigService.widgetConfigService.updateConfig({ config: newConfig }, true);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlAiChatFeedbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: HtmlAiChatFeedbackComponent, isStandalone: true, selector: "c8y-html-ai-chat-feedback", inputs: { step: "step" }, host: { classAttribute: "agent-step-feedback" }, usesOnChanges: true, ngImport: i0, template: "@if (!step.reasoning && !code) {\n  <div [innerHTML]=\"step.text | markdownToHtml | async\"></div>\n}\n@if (code) {\n  <div [innerHTML]=\"textBeforeCode | markdownToHtml | async\"></div>\n}\n@if (label) {\n  <c8y-list-group class=\"m-t-16 m-b-16\">\n    <c8y-li\n      [active]=\"!loading\"\n      [collapsed]=\"collapsed\"\n    >\n      <c8y-li-icon>\n        <span\n          class=\"btn-ai btn-ai-hint btn-sm\"\n          [ngClass]=\"{ working: loading }\"\n        >\n          <span></span>\n        </span>\n      </c8y-li-icon>\n      <c8y-li-body>\n        {{ label }}\n      </c8y-li-body>\n\n      @if (canCollapse) {\n        <c8y-li-collapse>\n          @if (step.reasoning) {\n            <div [innerHTML]=\"step.reasoning | markdownToHtml | async\"></div>\n          } @else if (code) {\n            <pre\n              class=\"fit-w\"\n              style=\"max-height: 320px\"\n              >{{ code }}</pre\n            >\n            @if (!loading) {\n              <button\n                class=\"btn btn-default btn-sm\"\n                [attr.aria-label]=\"'Revert to this version' | translate\"\n                [tooltip]=\"'Revert to this version' | translate\"\n                container=\"body\"\n                (click)=\"revert()\"\n              >\n                <i c8yIcon=\"undo\"></i>\n              </button>\n            }\n          } @else if (step) {\n            <pre\n              class=\"fit-w\"\n              style=\"max-height: 320px\"\n              >{{ step | json }}</pre\n            >\n          }\n        </c8y-li-collapse>\n      }\n    </c8y-li>\n  </c8y-list-group>\n}\n\n@if (code) {\n  <div [innerHTML]=\"textAfterCode | markdownToHtml | async\"></div>\n}\n", dependencies: [{ kind: "component", type: ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: ListItemCollapseComponent, selector: "c8y-list-item-collapse, c8y-li-collapse", inputs: ["collapseWay"] }, { kind: "component", type: ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "component", type: ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlAiChatFeedbackComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-ai-chat-feedback', imports: [
                        ListGroupComponent,
                        ListItemComponent,
                        ListItemCollapseComponent,
                        ListItemBodyComponent,
                        ListItemIconComponent,
                        NgClass,
                        JsonPipe,
                        MarkdownToHtmlPipe,
                        TooltipDirective,
                        AsyncPipe,
                        C8yTranslatePipe
                    ], standalone: true, host: { class: 'agent-step-feedback' }, template: "@if (!step.reasoning && !code) {\n  <div [innerHTML]=\"step.text | markdownToHtml | async\"></div>\n}\n@if (code) {\n  <div [innerHTML]=\"textBeforeCode | markdownToHtml | async\"></div>\n}\n@if (label) {\n  <c8y-list-group class=\"m-t-16 m-b-16\">\n    <c8y-li\n      [active]=\"!loading\"\n      [collapsed]=\"collapsed\"\n    >\n      <c8y-li-icon>\n        <span\n          class=\"btn-ai btn-ai-hint btn-sm\"\n          [ngClass]=\"{ working: loading }\"\n        >\n          <span></span>\n        </span>\n      </c8y-li-icon>\n      <c8y-li-body>\n        {{ label }}\n      </c8y-li-body>\n\n      @if (canCollapse) {\n        <c8y-li-collapse>\n          @if (step.reasoning) {\n            <div [innerHTML]=\"step.reasoning | markdownToHtml | async\"></div>\n          } @else if (code) {\n            <pre\n              class=\"fit-w\"\n              style=\"max-height: 320px\"\n              >{{ code }}</pre\n            >\n            @if (!loading) {\n              <button\n                class=\"btn btn-default btn-sm\"\n                [attr.aria-label]=\"'Revert to this version' | translate\"\n                [tooltip]=\"'Revert to this version' | translate\"\n                container=\"body\"\n                (click)=\"revert()\"\n              >\n                <i c8yIcon=\"undo\"></i>\n              </button>\n            }\n          } @else if (step) {\n            <pre\n              class=\"fit-w\"\n              style=\"max-height: 320px\"\n              >{{ step | json }}</pre\n            >\n          }\n        </c8y-li-collapse>\n      }\n    </c8y-li>\n  </c8y-list-group>\n}\n\n@if (code) {\n  <div [innerHTML]=\"textAfterCode | markdownToHtml | async\"></div>\n}\n" }]
        }], propDecorators: { step: [{
                type: Input,
                args: [{ required: true }]
            }] } });

class HtmlFrameComponent {
    constructor() {
        /**
         * If set to true, it will be ensured that a unique hash is generated
         * for every webcomponent. This is useful if configured as otherwise it might
         * happen that the same code is already used in another webcomponent and the
         * error messages can not be assigned correctly.
         */
        this.useSalt = false;
        this.alerts = [];
        this.sanitizer = inject(DomSanitizer);
        this.destroy$ = new Subject();
        this.hostElement = viewChild('hostElement', ...(ngDevMode ? [{ debugName: "hostElement" }] : []));
        this.reload$ = new Subject();
        this.htmlContentInitialization$ = this.reload$.pipe(filter(() => !!this.hostElement()), map(() => this.hostElement().nativeElement), switchMap((div) => merge(this.listenToErrors(), from(this.initDiv(div)).pipe(isEmpty$1(), filter(isEmpty => !!isEmpty), catchError(error => from([{ text: error, type: 'danger' }]))))), filter(alert => !!alert), takeUntil(this.destroy$));
        this.inventoryService = inject(InventoryService);
        this.htmlContentInitialization$.pipe(takeUntil(this.destroy$)).subscribe((alert) => {
            this.alerts.push(alert);
            console.error(alert.text);
        });
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    ngOnChanges(changes) {
        if (changes.config?.currentValue ||
            (this.config && changes.device?.previousValue !== changes.device?.currentValue)) {
            this.reloadComponent();
        }
    }
    reloadComponent() {
        this.alerts = [];
        const div = this.hostElement();
        div.nativeElement.innerHTML = '';
        this.reload$.next();
    }
    async initDiv(divHostElement) {
        const code = this.getCode();
        const hash = await this.generateHash(code, this.useSalt);
        const webComponentName = kebabCase(defaultWebComponentName) + hash;
        const context = await this.getContext(this.device);
        if (customElements.get(webComponentName)) {
            return this.createWebComponent(webComponentName, divHostElement, context);
        }
        const url = this.generateUrl(code);
        const defaultModule = await this.loadScript(url);
        // if the default module is a string, we will not use a webcomponent
        // instead we will simply parse the string and add it to the div
        // this is the case for legacy HTML widgets
        if (typeof defaultModule.default === 'string') {
            divHostElement.innerHTML = defaultModule.default;
            return EMPTY;
        }
        // same goes for an HTML Element
        if (defaultModule.default instanceof HTMLElement) {
            divHostElement.appendChild(defaultModule.default);
            // Find and execute scripts
            const scripts = divHostElement.querySelectorAll('script');
            scripts.forEach(script => {
                const newScript = document.createElement('script');
                Array.from(script.attributes).forEach(attr => {
                    newScript.setAttribute(attr.name, attr.value);
                });
                newScript.textContent = script.textContent;
                script.parentNode.replaceChild(newScript, script);
            });
            return EMPTY;
        }
        // as a race condition can happen on loading, we need
        // to check again if the web component is already defined
        if (!customElements.get(webComponentName)) {
            customElements.define(webComponentName, defaultModule.default);
        }
        this.createWebComponent(webComponentName, divHostElement, context);
        return EMPTY;
    }
    async getContext(device) {
        if (!device) {
            return;
        }
        if (!device.self) {
            const { data } = await this.inventoryService.detail(device.id);
            return data;
        }
        return device;
    }
    async loadScript(url) {
        const module = await import(/* webpackIgnore: true */ url);
        if (!module.default) {
            throw 'No default export found. Add an "export default" statement to your code.';
        }
        return module;
    }
    generateUrl(script) {
        const blob = new Blob([script], { type: 'application/javascript' });
        const url = URL.createObjectURL(blob);
        if (this.latestUrl) {
            URL.revokeObjectURL(this.latestUrl);
        }
        this.latestUrl = url;
        return url;
    }
    listenToErrors() {
        const errorEvents$ = fromEvent(window, 'error').pipe(filter(event => event.filename?.includes(this.latestUrl)), map(event => this.mapErrorEventToAlert(event)));
        const rejectionEvents$ = fromEvent(window, 'unhandledrejection').pipe(filter(event => event.reason?.stack?.includes(this.latestUrl)), map(event => this.mapErrorEventToAlert(event)), takeUntil(this.destroy$));
        return merge(errorEvents$, rejectionEvents$);
    }
    createWebComponent(webComponentName, divHostElement, context) {
        const webComponent = document.createElement(webComponentName);
        webComponent.c8yContext = context;
        divHostElement.appendChild(webComponent);
        return webComponent;
    }
    mapErrorEventToAlert(event) {
        const hasReason = 'reason' in event;
        if (hasReason && event.reason?.name === ReferenceError.name) {
            const undefinedVar = event.reason.message.split(' ')[0];
            return { text: undefinedVar + ' is not defined', type: 'info' };
        }
        return { text: hasReason ? event.reason.message : event.message, type: 'danger' };
    }
    getCode() {
        const isDevMode = this.config.devMode;
        if (isDevMode) {
            return this.config.code;
        }
        return this.createDefaultWebcomponentCode();
    }
    async generateHash(value, useSalt) {
        const encoder = new TextEncoder();
        const data = encoder.encode(value + (useSalt ? Math.random() : ''));
        const hashBuffer = await crypto.subtle.digest('SHA-256', data);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
        return hashHex;
    }
    createDefaultWebcomponentCode() {
        if (this.config.legacy) {
            const legacyWebComponent = legacyTemplate(this.config.options.advancedSecurity
                ? this.sanitizer.sanitize(SecurityContext.HTML, this.config.code)
                : this.config.code, this.device?.id, this.device?.name);
            return legacyWebComponent;
        }
        const webComponentScript = webComponentTemplate(this.config.options.advancedSecurity
            ? this.sanitizer.sanitize(SecurityContext.HTML, this.config.code)
            : this.config.code, this.config.options.advancedSecurity
            ? this.sanitizer.sanitize(SecurityContext.STYLE, this.config.css)
            : this.config.css, this.config.options.cssEncapsulation);
        return webComponentScript;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.16", type: HtmlFrameComponent, isStandalone: true, selector: "c8y-html-frame", inputs: { config: "config", device: "device", useSalt: "useSalt" }, host: { classAttribute: "d-contents" }, viewQueries: [{ propertyName: "hostElement", first: true, predicate: ["hostElement"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *ngFor=\"let alert of alerts\">\n  <div\n    class=\"alert m-8\"\n    role=\"alert\"\n    [ngClass]=\"{\n      'alert-danger': alert.type === 'danger',\n      'alert-warning': alert.type === 'warning',\n      'alert-info': alert.type === 'info',\n      'alert-success': alert.type === 'success'\n    }\"\n  >\n    <p><strong translate>There was an issue in the HTML widget:</strong></p>\n    <pre>{{ alert.text }}</pre>\n  </div>\n</ng-container>\n<div\n  class=\"fit-w fit-h\"\n  #hostElement\n></div>\n", dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlFrameComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [NgFor, NgClass], selector: 'c8y-html-frame', host: { class: 'd-contents' }, template: "<ng-container *ngFor=\"let alert of alerts\">\n  <div\n    class=\"alert m-8\"\n    role=\"alert\"\n    [ngClass]=\"{\n      'alert-danger': alert.type === 'danger',\n      'alert-warning': alert.type === 'warning',\n      'alert-info': alert.type === 'info',\n      'alert-success': alert.type === 'success'\n    }\"\n  >\n    <p><strong translate>There was an issue in the HTML widget:</strong></p>\n    <pre>{{ alert.text }}</pre>\n  </div>\n</ng-container>\n<div\n  class=\"fit-w fit-h\"\n  #hostElement\n></div>\n" }]
        }], ctorParameters: () => [], propDecorators: { config: [{
                type: Input
            }], device: [{
                type: Input
            }], useSalt: [{
                type: Input
            }], hostElement: [{ type: i0.ViewChild, args: ['hostElement', { isSignal: true }] }] } });

class WidgetCodeEditorComponent {
    constructor() {
        this.mode = 'code';
        this.configService = inject(HtmlWidgetConfigService);
        this.isAutoSaveEnabled = true;
        this.language = 'html';
        this.isLoading = false;
        this.TAB_WEBCOMPONENT_LABEL = gettext('Web Component`Tab label of HTML Widget`');
        this.TAB_HTML_LABEL = gettext('HTML`Tab label of HTML Widget`');
        this.TAB_CSS_LABEL = gettext('CSS`Tab label of HTML Widget`');
        this.BUTTON_DISABLE_AUTOSAVE_LABEL = gettext('Disable auto save`An action you can do on the html widget editor`');
        this.BUTTON_ENABLE_AUTOSAVE_LABEL = gettext('Enable auto save`An action you can do on the html widget editor`');
        this.TAB_OUTLET_NAME = 'html-widget-tab-outlet';
        this.destroy$ = new Subject();
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    ngOnChanges(changes) {
        if (changes.config) {
            this.changeCode(this.config.code);
        }
        if (changes.config?.currentValue) {
            this.loadCode();
        }
    }
    loadCode() {
        this.isLoading = true;
        const isInDevMode = this.config?.devMode;
        this.language = 'html';
        if (isInDevMode) {
            this.language = 'javascript';
        }
        if (this.mode === 'css') {
            this.language = 'css';
        }
        this.value = this.mode === 'code' ? this.config.code : this.config.css;
        this.isLoading = false;
        if (this.editor) {
            queueMicrotask(() => this.formatCode());
        }
    }
    switchMode(mode) {
        this.mode = mode;
        this.loadCode();
    }
    editorLoaded(editor) {
        this.editor = editor;
        const monaco = this.editorComponent.monaco;
        this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
            this.saveCode();
        });
    }
    formatCode() {
        this.editor.getAction('editor.action.formatDocument').run();
    }
    redo() {
        this.editor.trigger('keyboard', 'redo', null);
    }
    undo() {
        this.editor.trigger('keyboard', 'undo', null);
    }
    changeCode($event) {
        if (this.isAutoSaveEnabled) {
            this.saveCode($event);
        }
    }
    saveCode(codeStr) {
        const code = codeStr || this.editor.getValue();
        if (this.mode === 'code') {
            this.configService.changeCode(code);
            return;
        }
        this.configService.changeCss(code);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: WidgetCodeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: WidgetCodeEditorComponent, isStandalone: true, selector: "c8y-widget-code-editor", inputs: { mode: "mode", config: "config" }, viewQueries: [{ propertyName: "editorComponent", first: true, predicate: EditorComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-widget-config-feedback>\n  <div class=\"d-flex\">\n    @if (config?.devMode && !config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        translate\n      >\n        Advanced developer mode\n      </span>\n    }\n  </div>\n  <div class=\"d-flex\">\n    @if (config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        [title]=\"\n          'This widget is in legacy mode. Consider to upgrade this to a new HTML widget. Read our documentation on details to transform your widget'\n            | translate\n        \"\n        translate\n      >\n        Legacy mode\n      </span>\n    }\n  </div>\n</c8y-widget-config-feedback>\n\n<div class=\"d-flex d-col fit-h fit-w\">\n  <c8y-html-widget-advanced-settings\n    [devMode]=\"config?.devMode\"\n    [cssEncapsulation]=\"config?.options?.cssEncapsulation\"\n  ></c8y-html-widget-advanced-settings>\n\n  <fieldset class=\"c8y-fieldset p-0 overflow-hidden\">\n    <legend class=\"m-l-16 p-l-0\">{{ 'Code' | translate }}</legend>\n\n    <div class=\"btn-group btn-group-sm m-l-0 p-t-8 p-b-8 p-l-16 p-r-16 fit-w d-flex\">\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Undo' | translate\"\n        [tooltip]=\"'Undo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"undo()\"\n      >\n        <i [c8yIcon]=\"'undo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Redo' | translate\"\n        [tooltip]=\"'Redo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"redo()\"\n      >\n        <i [c8yIcon]=\"'redo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Format code' | translate\"\n        [tooltip]=\"'Format code' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"formatCode()\"\n      >\n        <i [c8yIcon]=\"'format-align-left'\"></i>\n      </button>\n\n      <label class=\"c8y-switch m-l-auto\">\n        <input\n          type=\"checkbox\"\n          [checked]=\"isAutoSaveEnabled\"\n          (change)=\"isAutoSaveEnabled = !isAutoSaveEnabled\"\n        />\n        <span></span>\n        <span translate>Auto save</span>\n      </label>\n    </div>\n\n    <div\n      class=\"btn-toolbar m-0 p-relative\"\n      role=\"toolbar\"\n    >\n      <c8y-tabs-outlet\n        class=\"elevation-none\"\n        [outletName]=\"TAB_OUTLET_NAME\"\n        [orientation]=\"'horizontal'\"\n        [openFirstTab]=\"false\"\n      ></c8y-tabs-outlet>\n      <c8y-tab\n        [icon]=\"'code'\"\n        [label]=\"(config?.devMode ? TAB_WEBCOMPONENT_LABEL : TAB_HTML_LABEL) | translate\"\n        [priority]=\"100\"\n        [showAlways]=\"true\"\n        [tabsOutlet]=\"TAB_OUTLET_NAME\"\n        [isActive]=\"mode === 'code'\"\n        (onSelect)=\"switchMode('code')\"\n      ></c8y-tab>\n      @if (!config?.devMode && !config?.legacy) {\n        <c8y-tab\n          [icon]=\"'c8y-css'\"\n          [label]=\"TAB_CSS_LABEL | translate\"\n          [priority]=\"0\"\n          [tabsOutlet]=\"TAB_OUTLET_NAME\"\n          [isActive]=\"mode === 'css'\"\n          (onSelect)=\"switchMode('css')\"\n        ></c8y-tab>\n      }\n    </div>\n\n    @if (!isLoading) {\n      @if (!(mode === 'css' && config?.devMode)) {\n        <c8y-editor\n          class=\"flex-grow d-block\"\n          style=\"height: 450px\"\n          [ngModel]=\"value\"\n          (ngModelChange)=\"changeCode($event)\"\n          [editorOptions]=\"{\n            language,\n            tabSize: 2,\n            insertSpaces: true,\n            minimap: { enabled: false }\n          }\"\n          (editorInit)=\"editorLoaded($event)\"\n        ></c8y-editor>\n      }\n    } @else {\n      <c8y-loading></c8y-loading>\n    }\n  </fieldset>\n</div>\n", dependencies: [{ kind: "component", type: EditorComponent, selector: "c8y-editor", inputs: ["editorOptions", "theme"], outputs: ["editorInit"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: WidgetConfigFeedbackComponent, selector: "c8y-widget-config-feedback" }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2$1.TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: ["tabs", "orientation", "navigatorOpen", "outletName", "context", "openFirstTab", "hasHeader"] }, { kind: "component", type: i2$1.TabComponent, selector: "c8y-tab", inputs: ["path", "label", "icon", "priority", "orientation", "injector", "tabsOutlet", "isActive", "text", "showAlways"], outputs: ["onSelect"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: AdvancedSettingsComponent, selector: "c8y-html-widget-advanced-settings", inputs: ["devMode", "cssEncapsulation"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: WidgetCodeEditorComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [
                        EditorComponent,
                        FormsModule,
                        IconDirective,
                        C8yTranslatePipe,
                        WidgetConfigFeedbackComponent,
                        TabsModule,
                        TooltipModule,
                        PopoverModule,
                        LoadingComponent,
                        AdvancedSettingsComponent
                    ], selector: 'c8y-widget-code-editor', template: "<c8y-widget-config-feedback>\n  <div class=\"d-flex\">\n    @if (config?.devMode && !config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        translate\n      >\n        Advanced developer mode\n      </span>\n    }\n  </div>\n  <div class=\"d-flex\">\n    @if (config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        [title]=\"\n          'This widget is in legacy mode. Consider to upgrade this to a new HTML widget. Read our documentation on details to transform your widget'\n            | translate\n        \"\n        translate\n      >\n        Legacy mode\n      </span>\n    }\n  </div>\n</c8y-widget-config-feedback>\n\n<div class=\"d-flex d-col fit-h fit-w\">\n  <c8y-html-widget-advanced-settings\n    [devMode]=\"config?.devMode\"\n    [cssEncapsulation]=\"config?.options?.cssEncapsulation\"\n  ></c8y-html-widget-advanced-settings>\n\n  <fieldset class=\"c8y-fieldset p-0 overflow-hidden\">\n    <legend class=\"m-l-16 p-l-0\">{{ 'Code' | translate }}</legend>\n\n    <div class=\"btn-group btn-group-sm m-l-0 p-t-8 p-b-8 p-l-16 p-r-16 fit-w d-flex\">\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Undo' | translate\"\n        [tooltip]=\"'Undo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"undo()\"\n      >\n        <i [c8yIcon]=\"'undo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Redo' | translate\"\n        [tooltip]=\"'Redo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"redo()\"\n      >\n        <i [c8yIcon]=\"'redo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Format code' | translate\"\n        [tooltip]=\"'Format code' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"formatCode()\"\n      >\n        <i [c8yIcon]=\"'format-align-left'\"></i>\n      </button>\n\n      <label class=\"c8y-switch m-l-auto\">\n        <input\n          type=\"checkbox\"\n          [checked]=\"isAutoSaveEnabled\"\n          (change)=\"isAutoSaveEnabled = !isAutoSaveEnabled\"\n        />\n        <span></span>\n        <span translate>Auto save</span>\n      </label>\n    </div>\n\n    <div\n      class=\"btn-toolbar m-0 p-relative\"\n      role=\"toolbar\"\n    >\n      <c8y-tabs-outlet\n        class=\"elevation-none\"\n        [outletName]=\"TAB_OUTLET_NAME\"\n        [orientation]=\"'horizontal'\"\n        [openFirstTab]=\"false\"\n      ></c8y-tabs-outlet>\n      <c8y-tab\n        [icon]=\"'code'\"\n        [label]=\"(config?.devMode ? TAB_WEBCOMPONENT_LABEL : TAB_HTML_LABEL) | translate\"\n        [priority]=\"100\"\n        [showAlways]=\"true\"\n        [tabsOutlet]=\"TAB_OUTLET_NAME\"\n        [isActive]=\"mode === 'code'\"\n        (onSelect)=\"switchMode('code')\"\n      ></c8y-tab>\n      @if (!config?.devMode && !config?.legacy) {\n        <c8y-tab\n          [icon]=\"'c8y-css'\"\n          [label]=\"TAB_CSS_LABEL | translate\"\n          [priority]=\"0\"\n          [tabsOutlet]=\"TAB_OUTLET_NAME\"\n          [isActive]=\"mode === 'css'\"\n          (onSelect)=\"switchMode('css')\"\n        ></c8y-tab>\n      }\n    </div>\n\n    @if (!isLoading) {\n      @if (!(mode === 'css' && config?.devMode)) {\n        <c8y-editor\n          class=\"flex-grow d-block\"\n          style=\"height: 450px\"\n          [ngModel]=\"value\"\n          (ngModelChange)=\"changeCode($event)\"\n          [editorOptions]=\"{\n            language,\n            tabSize: 2,\n            insertSpaces: true,\n            minimap: { enabled: false }\n          }\"\n          (editorInit)=\"editorLoaded($event)\"\n        ></c8y-editor>\n      }\n    } @else {\n      <c8y-loading></c8y-loading>\n    }\n  </fieldset>\n</div>\n" }]
        }], propDecorators: { mode: [{
                type: Input
            }], config: [{
                type: Input
            }], editorComponent: [{
                type: ViewChild,
                args: [EditorComponent]
            }] } });

class HtmlWidgetConfigComponent {
    constructor() {
        this.options = inject(OptionsService);
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
        this.widgetConfigService = inject(WidgetConfigService);
    }
    set htmlPreviewTemplate(template) {
        if (template) {
            this.widgetConfigService.setPreview(template);
        }
        else {
            this.widgetConfigService.setPreview(null);
        }
    }
    ngOnDestroy() {
        // sadly the service is component scoped
        // but still not recycled correctly. That is why we do
        // it here.
        this.htmlWidgetConfigService.destroy();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: HtmlWidgetConfigComponent, isStandalone: true, selector: "c8y-html-widget-config", viewQueries: [{ propertyName: "htmlPreviewTemplate", first: true, predicate: ["htmlPreview"], descendants: true }], ngImport: i0, template: "<c8y-widget-code-editor\n  [config]=\"htmlWidgetConfigService.config$ | async\"\n  [mode]=\"'code'\"\n></c8y-widget-code-editor>\n\n<ng-template #htmlPreview>\n  <c8y-html-frame\n    [config]=\"htmlWidgetConfigService.codeEditorChangeConfig$ | async\"\n    [device]=\"(widgetConfigService.currentConfig$ | async).device\"\n    [useSalt]=\"true\"\n  ></c8y-html-frame>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: HtmlFrameComponent, selector: "c8y-html-frame", inputs: ["config", "device", "useSalt"] }, { kind: "component", type: WidgetCodeEditorComponent, selector: "c8y-widget-code-editor", inputs: ["mode", "config"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget-config', standalone: true, imports: [RouterModule, FormsModule, AsyncPipe, HtmlFrameComponent, WidgetCodeEditorComponent], template: "<c8y-widget-code-editor\n  [config]=\"htmlWidgetConfigService.config$ | async\"\n  [mode]=\"'code'\"\n></c8y-widget-code-editor>\n\n<ng-template #htmlPreview>\n  <c8y-html-frame\n    [config]=\"htmlWidgetConfigService.codeEditorChangeConfig$ | async\"\n    [device]=\"(widgetConfigService.currentConfig$ | async).device\"\n    [useSalt]=\"true\"\n  ></c8y-html-frame>\n</ng-template>\n" }]
        }], propDecorators: { htmlPreviewTemplate: [{
                type: ViewChild,
                args: ['htmlPreview']
            }] } });

class HtmlWidgetPropertiesSelectorComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.clipboardService = inject(ClipboardService);
    }
    /**
     * Copies the property path to the clipboard in a format suitable for use in HTML widget.
     * For nested properties, it uses the keyPath to create a path to nested property.
     * @param context The context containing the property information.
     */
    async copyProperty(context) {
        const nonAlphanumericRegex = /[^a-zA-Z0-9]/;
        let path;
        if (context.keyPath) {
            path = context.keyPath
                .map(key => (nonAlphanumericRegex.test(key) ? `['${key}']` : `.${key}`))
                .join('');
        }
        else {
            path = nonAlphanumericRegex.test(context.name) ? `['${context.name}']` : `.${context.name}`;
        }
        const content = '${this.c8yContext' + path + '}';
        await this.clipboardService.writeText(content);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetPropertiesSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: HtmlWidgetPropertiesSelectorComponent, isStandalone: true, selector: "c8y-html-widget-properties-selector", host: { classAttribute: "bg-level-1" }, ngImport: i0, template: "<div\n  class=\"d-flex m-b-8\"\n  style=\"margin-top: -16px\"\n>\n  <em\n    class=\"m-l-24 text-muted\"\n    translate\n  >\n    How to use properties in the widget\n  </em>\n  <button\n    class=\"btn-help btn-help--sm\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"helpContent\"\n    placement=\"right\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  ></button>\n</div>\n<c8y-asset-property-list\n  class=\"inner-scroll bg-inherit d-block\"\n  style=\"max-height: 450px\"\n  [asset]=\"(widgetConfigService.currentConfig$ | async).device\"\n  [config]=\"{\n    selectMode: 'none',\n    expansionMode: 'expandedByDefault',\n    filterable: false,\n    allowDragAndDrop: true\n  }\"\n>\n  <button\n    class=\"btn-dot btn fit-h\"\n    [attr.aria-label]=\"'Copy' | translate\"\n    tooltip=\"{{ 'Copy' | translate }}\"\n    type=\"button\"\n    *c8yAssetPropertyAction=\"let context\"\n    [delay]=\"500\"\n    (click)=\"copyProperty(context)\"\n  >\n    <i class=\"dlt-c8y-icon-copy\"></i>\n  </button>\n</c8y-asset-property-list>\n\n<ng-template #helpContent>\n  <p\n    class=\"p-b-8\"\n    translate\n  >\n    Click the copy icon next to a property, then paste it into the HTML editor below as a template\n    literal.\n  </p>\n</ng-template>\n", dependencies: [{ kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "extraProperties"], outputs: ["selectedProperties"] }, { kind: "directive", type: AssetPropertyActionDirective, selector: "[c8yAssetPropertyAction]" }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetPropertiesSelectorComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget-properties-selector', host: {
                        class: 'bg-level-1'
                    }, standalone: true, imports: [
                        AssetPropertyListComponent,
                        AssetPropertyActionDirective,
                        AsyncPipe,
                        C8yTranslatePipe,
                        PopoverModule,
                        TooltipModule
                    ], template: "<div\n  class=\"d-flex m-b-8\"\n  style=\"margin-top: -16px\"\n>\n  <em\n    class=\"m-l-24 text-muted\"\n    translate\n  >\n    How to use properties in the widget\n  </em>\n  <button\n    class=\"btn-help btn-help--sm\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"helpContent\"\n    placement=\"right\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  ></button>\n</div>\n<c8y-asset-property-list\n  class=\"inner-scroll bg-inherit d-block\"\n  style=\"max-height: 450px\"\n  [asset]=\"(widgetConfigService.currentConfig$ | async).device\"\n  [config]=\"{\n    selectMode: 'none',\n    expansionMode: 'expandedByDefault',\n    filterable: false,\n    allowDragAndDrop: true\n  }\"\n>\n  <button\n    class=\"btn-dot btn fit-h\"\n    [attr.aria-label]=\"'Copy' | translate\"\n    tooltip=\"{{ 'Copy' | translate }}\"\n    type=\"button\"\n    *c8yAssetPropertyAction=\"let context\"\n    [delay]=\"500\"\n    (click)=\"copyProperty(context)\"\n  >\n    <i class=\"dlt-c8y-icon-copy\"></i>\n  </button>\n</c8y-asset-property-list>\n\n<ng-template #helpContent>\n  <p\n    class=\"p-b-8\"\n    translate\n  >\n    Click the copy icon next to a property, then paste it into the HTML editor below as a template\n    literal.\n  </p>\n</ng-template>\n" }]
        }] });

class HtmlWidgetComponent {
    ngOnInit() {
        if (this.config.html && !this.config.config) {
            this.config.config = this.mapLegacyConfig(this.config);
        }
    }
    mapLegacyConfig(current) {
        const isAlreadyInAdvancedMode = current?.config?.devMode === true;
        if (isAlreadyInAdvancedMode) {
            return current.config;
        }
        return {
            code: current.html,
            css: '',
            legacy: true,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity: current.sanitization === 'strict'
            }
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: HtmlWidgetComponent, isStandalone: true, selector: "c8y-html-widget", inputs: { config: "config" }, ngImport: i0, template: "<c8y-html-frame\n  [config]=\"config.config\"\n  [device]=\"config.device\"\n></c8y-html-frame>\n", dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: HtmlFrameComponent, selector: "c8y-html-frame", inputs: ["config", "device", "useSalt"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: HtmlWidgetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget', standalone: true, imports: [RouterModule, HtmlFrameComponent], template: "<c8y-html-frame\n  [config]=\"config.config\"\n  [device]=\"config.device\"\n></c8y-html-frame>\n" }]
        }], propDecorators: { config: [{
                type: Input
            }] } });

/**
 * Generated bundle index. Do not edit.
 */

export { AdvancedSettingsComponent, HtmlAiChatFeedbackComponent, HtmlFrameComponent, HtmlWidgetComponent, HtmlWidgetConfigComponent, HtmlWidgetConfigService, HtmlWidgetPropertiesSelectorComponent, INITIAL_CSS_FORMATTED, INITIAL_HTML_FORMATTED, WidgetCodeEditorComponent, defaultWebComponentAttributeNameContext, defaultWebComponentName, legacyTemplate, webComponentTemplate };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-html-widget.mjs.map
