Commit 0c1d5b9cc061650f3a2fb9b069f5cf60087eaed9

Authored by Igor Kulikov
Committed by GitHub
2 parents b3534942 6f4d837a

Merge pull request #5450 from deaflynx/protobuf-ace-editor

[3.3.2] UI: Added ace editor for protobuf content
@@ -47,35 +47,50 @@ @@ -47,35 +47,50 @@
47 </mat-error> 47 </mat-error>
48 </mat-form-field> 48 </mat-form-field>
49 <div *ngIf="protoPayloadType" fxLayout="column"> 49 <div *ngIf="protoPayloadType" fxLayout="column">
50 - <mat-form-field fxFlex>  
51 - <mat-label translate>device-profile.telemetry-proto-schema</mat-label>  
52 - <textarea matInput required formControlName="deviceTelemetryProtoSchema" rows="5"></textarea> 50 + <ng-container>
  51 + <tb-protobuf-content
  52 + fxFlex
  53 + formControlName="deviceTelemetryProtoSchema"
  54 + label="{{ 'device-profile.telemetry-proto-schema' | translate }}"
  55 + [fillHeight]="true">
  56 + </tb-protobuf-content>
53 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')"> 57 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')">
54 {{ 'device-profile.telemetry-proto-schema-required' | translate}} 58 {{ 'device-profile.telemetry-proto-schema-required' | translate}}
55 </mat-error> 59 </mat-error>
56 - </mat-form-field>  
57 - <mat-form-field fxFlex>  
58 - <mat-label translate>device-profile.attributes-proto-schema</mat-label>  
59 - <textarea matInput required formControlName="deviceAttributesProtoSchema" rows="5"></textarea> 60 + </ng-container>
  61 + <ng-container>
  62 + <tb-protobuf-content
  63 + fxFlex
  64 + formControlName="deviceAttributesProtoSchema"
  65 + label="{{ 'device-profile.attributes-proto-schema' | translate }}"
  66 + [fillHeight]="true">
  67 + </tb-protobuf-content>
60 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')"> 68 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')">
61 {{ 'device-profile.attributes-proto-schema-required' | translate}} 69 {{ 'device-profile.attributes-proto-schema-required' | translate}}
62 </mat-error> 70 </mat-error>
63 - </mat-form-field>  
64 - <mat-form-field style="padding-bottom: 20px" fxFlex>  
65 - <mat-label translate>device-profile.rpc-request-proto-schema</mat-label>  
66 - <textarea matInput required formControlName="deviceRpcRequestProtoSchema" rows="5"></textarea> 71 + </ng-container>
  72 + <ng-container>
  73 + <tb-protobuf-content
  74 + fxFlex
  75 + formControlName="deviceRpcRequestProtoSchema"
  76 + label="{{ 'device-profile.rpc-request-proto-schema' | translate }}"
  77 + [fillHeight]="true">
  78 + </tb-protobuf-content>
67 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')"> 79 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')">
68 {{ 'device-profile.rpc-request-proto-schema-required' | translate}} 80 {{ 'device-profile.rpc-request-proto-schema-required' | translate}}
69 </mat-error> 81 </mat-error>
70 - <mat-hint class="tb-hint" translate>device-profile.rpc-request-proto-schema-hint</mat-hint>  
71 - </mat-form-field>  
72 - <mat-form-field fxFlex>  
73 - <mat-label translate>device-profile.rpc-response-proto-schema</mat-label>  
74 - <textarea matInput required formControlName="deviceRpcResponseProtoSchema" rows="5"></textarea> 82 + </ng-container>
  83 + <ng-container>
  84 + <tb-protobuf-content
  85 + fxFlex
  86 + formControlName="deviceRpcResponseProtoSchema"
  87 + label="{{ 'device-profile.rpc-response-proto-schema' | translate }}"
  88 + [fillHeight]="true">
  89 + </tb-protobuf-content>
75 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')"> 90 <mat-error *ngIf="coapTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')">
76 {{ 'device-profile.rpc-response-proto-schema-required' | translate}} 91 {{ 'device-profile.rpc-response-proto-schema-required' | translate}}
77 </mat-error> 92 </mat-error>
78 - </mat-form-field> 93 + </ng-container>
79 </div> 94 </div>
80 </div> 95 </div>
81 </fieldset> 96 </fieldset>
@@ -86,35 +86,50 @@ @@ -86,35 +86,50 @@
86 </div> 86 </div>
87 </div> 87 </div>
88 <div *ngIf="protoPayloadType" fxLayout="column"> 88 <div *ngIf="protoPayloadType" fxLayout="column">
89 - <mat-form-field fxFlex>  
90 - <mat-label translate>device-profile.telemetry-proto-schema</mat-label>  
91 - <textarea matInput required formControlName="deviceTelemetryProtoSchema" rows="5"></textarea> 89 + <ng-container>
  90 + <tb-protobuf-content
  91 + fxFlex
  92 + formControlName="deviceTelemetryProtoSchema"
  93 + label="{{ 'device-profile.telemetry-proto-schema' | translate }}"
  94 + [fillHeight]="true">
  95 + </tb-protobuf-content>
92 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')"> 96 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceTelemetryProtoSchema').hasError('required')">
93 {{ 'device-profile.telemetry-proto-schema-required' | translate}} 97 {{ 'device-profile.telemetry-proto-schema-required' | translate}}
94 </mat-error> 98 </mat-error>
95 - </mat-form-field>  
96 - <mat-form-field fxFlex>  
97 - <mat-label translate>device-profile.attributes-proto-schema</mat-label>  
98 - <textarea matInput required formControlName="deviceAttributesProtoSchema" rows="5"></textarea> 99 + </ng-container>
  100 + <ng-container>
  101 + <tb-protobuf-content
  102 + fxFlex
  103 + formControlName="deviceAttributesProtoSchema"
  104 + label="{{ 'device-profile.attributes-proto-schema' | translate }}"
  105 + [fillHeight]="true">
  106 + </tb-protobuf-content>
99 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')"> 107 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceAttributesProtoSchema').hasError('required')">
100 {{ 'device-profile.attributes-proto-schema-required' | translate}} 108 {{ 'device-profile.attributes-proto-schema-required' | translate}}
101 </mat-error> 109 </mat-error>
102 - </mat-form-field>  
103 - <mat-form-field style="padding-bottom: 20px" fxFlex>  
104 - <mat-label translate>device-profile.rpc-request-proto-schema</mat-label>  
105 - <textarea matInput required formControlName="deviceRpcRequestProtoSchema" rows="5"></textarea> 110 + </ng-container>
  111 + <ng-container>
  112 + <tb-protobuf-content
  113 + fxFlex
  114 + formControlName="deviceRpcRequestProtoSchema"
  115 + label="{{ 'device-profile.rpc-request-proto-schema' | translate }}"
  116 + [fillHeight]="true">
  117 + </tb-protobuf-content>
106 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')"> 118 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcRequestProtoSchema').hasError('required')">
107 {{ 'device-profile.rpc-request-proto-schema-required' | translate}} 119 {{ 'device-profile.rpc-request-proto-schema-required' | translate}}
108 </mat-error> 120 </mat-error>
109 - <mat-hint class="tb-hint" translate>device-profile.rpc-request-proto-schema-hint</mat-hint>  
110 - </mat-form-field>  
111 - <mat-form-field fxFlex>  
112 - <mat-label translate>device-profile.rpc-response-proto-schema</mat-label>  
113 - <textarea matInput required formControlName="deviceRpcResponseProtoSchema" rows="5"></textarea> 121 + </ng-container>
  122 + <ng-container>
  123 + <tb-protobuf-content
  124 + fxFlex
  125 + formControlName="deviceRpcResponseProtoSchema"
  126 + label="{{ 'device-profile.rpc-response-proto-schema' | translate }}"
  127 + [fillHeight]="true">
  128 + </tb-protobuf-content>
114 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')"> 129 <mat-error *ngIf="mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.deviceRpcResponseProtoSchema').hasError('required')">
115 {{ 'device-profile.rpc-response-proto-schema-required' | translate}} 130 {{ 'device-profile.rpc-response-proto-schema-required' | translate}}
116 </mat-error> 131 </mat-error>
117 - </mat-form-field> 132 + </ng-container>
118 </div> 133 </div>
119 </div> 134 </div>
120 </fieldset> 135 </fieldset>
  1 +<!--
  2 +
  3 + Copyright © 2016-2021 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div style="background: #fff;" [ngClass]="{'fill-height': fillHeight}"
  19 + tb-fullscreen
  20 + [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
  21 + <div fxLayout="row" fxLayoutAlign="start center" style="height: 40px;" class="tb-protobuf-content-toolbar">
  22 + <label class="tb-title no-padding">{{ label }}</label>
  23 + <span fxFlex></span>
  24 + <button type="button"
  25 + mat-button *ngIf="!readonly && !disabled" class="tidy" (click)="beautifyProtobuf()">
  26 + {{'js-func.tidy' | translate }}
  27 + </button>
  28 + <fieldset style="width: initial">
  29 + <div matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}"
  30 + matTooltipPosition="above"
  31 + style="border-radius: 50%"
  32 + (click)="fullscreen = !fullscreen">
  33 + <button type='button' mat-button mat-icon-button class="tb-mat-32">
  34 + <mat-icon class="material-icons">{{ fullscreen ? 'fullscreen_exit' : 'fullscreen' }}</mat-icon>
  35 + </button>
  36 + </div>
  37 + </fieldset>
  38 + </div>
  39 + <div id="tb-protobuf-panel" tb-toast toastTarget="{{toastTargetId}}"
  40 + class="tb-protobuf-content-panel" fxLayout="column">
  41 + <div #protobufEditor id="tb-protobuf-input" [ngStyle]="editorStyle" [ngClass]="{'fill-height': fillHeight}"></div>
  42 + </div>
  43 +</div>
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +:host {
  17 + position: relative;
  18 +
  19 + .fill-height {
  20 + height: 100%;
  21 + }
  22 +}
  23 +
  24 +.tb-protobuf-content-toolbar {
  25 + button.mat-button, button.mat-icon-button, button.mat-icon-button.tb-mat-32 {
  26 + align-items: center;
  27 + vertical-align: middle;
  28 + min-width: 32px;
  29 + min-height: 15px;
  30 + padding: 4px;
  31 + margin: 0;
  32 + font-size: .8rem;
  33 + line-height: 15px;
  34 + color: #7b7b7b;
  35 + background: rgba(220, 220, 220, .35);
  36 + &:not(:last-child) {
  37 + margin-right: 4px;
  38 + }
  39 + }
  40 +}
  41 +
  42 +.tb-protobuf-content-panel {
  43 + height: 100%;
  44 + margin-left: 15px;
  45 + border: 1px solid #c0c0c0;
  46 +
  47 + #tb-protobuf-input {
  48 + width: 100%;
  49 + min-width: 200px;
  50 + min-height: 160px;
  51 + height: 100%;
  52 +
  53 + &:not(.fill-height) {
  54 + min-height: 200px;
  55 + }
  56 + }
  57 +}
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import {
  18 + Component,
  19 + ElementRef,
  20 + forwardRef,
  21 + Input,
  22 + OnDestroy,
  23 + OnInit,
  24 + ViewChild
  25 +} from '@angular/core';
  26 +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
  27 +import { Ace } from 'ace-builds';
  28 +import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
  29 +import { ResizeObserver } from '@juggle/resize-observer';
  30 +import { guid } from '@core/utils';
  31 +import { coerceBooleanProperty } from '@angular/cdk/coercion';
  32 +import { Store } from '@ngrx/store';
  33 +import { AppState } from '@core/core.state';
  34 +import { getAce } from '@shared/models/ace/ace.models';
  35 +import { beautifyJs } from '@shared/models/beautify.models';
  36 +
  37 +@Component({
  38 + selector: 'tb-protobuf-content',
  39 + templateUrl: './protobuf-content.component.html',
  40 + styleUrls: ['./protobuf-content.component.scss'],
  41 + providers: [
  42 + {
  43 + provide: NG_VALUE_ACCESSOR,
  44 + useExisting: forwardRef(() => ProtobufContentComponent),
  45 + multi: true
  46 + }
  47 + ]
  48 +})
  49 +export class ProtobufContentComponent implements OnInit, ControlValueAccessor, OnDestroy {
  50 +
  51 + @ViewChild('protobufEditor', {static: true})
  52 + protobufEditorElmRef: ElementRef;
  53 +
  54 + private protobufEditor: Ace.Editor;
  55 + private editorsResizeCaf: CancelAnimationFrame;
  56 + private editorResize$: ResizeObserver;
  57 + private ignoreChange = false;
  58 +
  59 + toastTargetId = `protobufContentEditor-${guid()}`;
  60 +
  61 + @Input() label: string;
  62 +
  63 + @Input() disabled: boolean;
  64 +
  65 + @Input() fillHeight: boolean;
  66 +
  67 + @Input() editorStyle: {[klass: string]: any};
  68 +
  69 + @Input() tbPlaceholder: string;
  70 +
  71 + private readonlyValue: boolean;
  72 + get readonly(): boolean {
  73 + return this.readonlyValue;
  74 + }
  75 + @Input()
  76 + set readonly(value: boolean) {
  77 + this.readonlyValue = coerceBooleanProperty(value);
  78 + }
  79 +
  80 + fullscreen = false;
  81 +
  82 + contentBody: string;
  83 +
  84 + errorShowed = false;
  85 +
  86 + private propagateChange = null;
  87 +
  88 + constructor(public elementRef: ElementRef,
  89 + protected store: Store<AppState>,
  90 + private raf: RafService) {
  91 + }
  92 +
  93 + ngOnInit(): void {
  94 + const editorElement = this.protobufEditorElmRef.nativeElement;
  95 + let editorOptions: Partial<Ace.EditorOptions> = {
  96 + mode: `ace/mode/protobuf`,
  97 + showGutter: true,
  98 + showPrintMargin: false,
  99 + readOnly: this.disabled || this.readonly,
  100 + };
  101 +
  102 + const advancedOptions = {
  103 + enableSnippets: true,
  104 + enableBasicAutocompletion: true,
  105 + enableLiveAutocompletion: true
  106 + };
  107 +
  108 + editorOptions = {...editorOptions, ...advancedOptions};
  109 + getAce().subscribe(
  110 + (ace) => {
  111 + this.protobufEditor = ace.edit(editorElement, editorOptions);
  112 + this.protobufEditor.session.setUseWrapMode(true);
  113 + this.protobufEditor.setValue(this.contentBody ? this.contentBody : '', -1);
  114 + this.protobufEditor.setReadOnly(this.disabled || this.readonly);
  115 + this.protobufEditor.on('change', () => {
  116 + if (!this.ignoreChange) {
  117 + this.updateView();
  118 + }
  119 + });
  120 + this.editorResize$ = new ResizeObserver(() => {
  121 + this.onAceEditorResize();
  122 + });
  123 + this.editorResize$.observe(editorElement);
  124 + }
  125 + );
  126 + }
  127 +
  128 + ngOnDestroy(): void {
  129 + if (this.editorResize$) {
  130 + this.editorResize$.disconnect();
  131 + }
  132 + }
  133 +
  134 + registerOnChange(fn: any): void {
  135 + this.propagateChange = fn;
  136 + }
  137 +
  138 + registerOnTouched(fn: any): void {
  139 + }
  140 +
  141 + setDisabledState(isDisabled: boolean): void {
  142 + this.disabled = isDisabled;
  143 + if (this.protobufEditor) {
  144 + this.protobufEditor.setReadOnly(this.disabled || this.readonly);
  145 + }
  146 + }
  147 +
  148 + writeValue(value: string): void {
  149 + this.contentBody = value;
  150 + if (this.protobufEditor) {
  151 + this.ignoreChange = true;
  152 + this.protobufEditor.setValue(this.contentBody ? this.contentBody : '', -1);
  153 + this.ignoreChange = false;
  154 + }
  155 + }
  156 +
  157 + updateView() {
  158 + const editorValue = this.protobufEditor.getValue();
  159 + if (this.contentBody !== editorValue) {
  160 + this.contentBody = editorValue;
  161 + this.propagateChange(this.contentBody);
  162 + }
  163 + }
  164 +
  165 + beautifyProtobuf() {
  166 + beautifyJs(this.contentBody, {indent_size: 4, wrap_line_length: 60}).subscribe(
  167 + (res) => {
  168 + this.protobufEditor.setValue(res ? res : '', -1);
  169 + this.updateView();
  170 + }
  171 + );
  172 + }
  173 +
  174 + onFullscreen() {
  175 + if (this.protobufEditor) {
  176 + setTimeout(() => {
  177 + this.protobufEditor.resize();
  178 + }, 0);
  179 + }
  180 + }
  181 +
  182 + private onAceEditorResize() {
  183 + if (this.editorsResizeCaf) {
  184 + this.editorsResizeCaf();
  185 + this.editorsResizeCaf = null;
  186 + }
  187 + this.editorsResizeCaf = this.raf.raf(() => {
  188 + this.protobufEditor.resize();
  189 + this.protobufEditor.renderer.updateFull();
  190 + });
  191 + }
  192 +
  193 +}
@@ -36,6 +36,8 @@ export function loadAceDependencies(): Observable<any> { @@ -36,6 +36,8 @@ export function loadAceDependencies(): Observable<any> {
36 aceObservables.push(from(import('ace-builds/src-noconflict/mode-text'))); 36 aceObservables.push(from(import('ace-builds/src-noconflict/mode-text')));
37 aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown'))); 37 aceObservables.push(from(import('ace-builds/src-noconflict/mode-markdown')));
38 aceObservables.push(from(import('ace-builds/src-noconflict/mode-html'))); 38 aceObservables.push(from(import('ace-builds/src-noconflict/mode-html')));
  39 + aceObservables.push(from(import('ace-builds/src-noconflict/mode-c_cpp')));
  40 + aceObservables.push(from(import('ace-builds/src-noconflict/mode-protobuf')));
39 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java'))); 41 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/java')));
40 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css'))); 42 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/css')));
41 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/json'))); 43 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/json')));
@@ -43,6 +45,8 @@ export function loadAceDependencies(): Observable<any> { @@ -43,6 +45,8 @@ export function loadAceDependencies(): Observable<any> {
43 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/text'))); 45 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/text')));
44 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/markdown'))); 46 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/markdown')));
45 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/html'))); 47 aceObservables.push(from(import('ace-builds/src-noconflict/snippets/html')));
  48 + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/c_cpp')));
  49 + aceObservables.push(from(import('ace-builds/src-noconflict/snippets/protobuf')));
46 aceObservables.push(from(import('ace-builds/src-noconflict/theme-textmate'))); 50 aceObservables.push(from(import('ace-builds/src-noconflict/theme-textmate')));
47 aceObservables.push(from(import('ace-builds/src-noconflict/theme-github'))); 51 aceObservables.push(from(import('ace-builds/src-noconflict/theme-github')));
48 return forkJoin(aceObservables).pipe( 52 return forkJoin(aceObservables).pipe(
@@ -155,6 +155,7 @@ import { MarkedOptionsService } from '@shared/components/marked-options.service' @@ -155,6 +155,7 @@ import { MarkedOptionsService } from '@shared/components/marked-options.service'
155 import { TbPopoverService } from '@shared/components/popover.service'; 155 import { TbPopoverService } from '@shared/components/popover.service';
156 import { HELP_MARKDOWN_COMPONENT_TOKEN, SHARED_MODULE_TOKEN } from '@shared/components/tokens'; 156 import { HELP_MARKDOWN_COMPONENT_TOKEN, SHARED_MODULE_TOKEN } from '@shared/components/tokens';
157 import { TbMarkdownComponent } from '@shared/components/markdown.component'; 157 import { TbMarkdownComponent } from '@shared/components/markdown.component';
  158 +import { ProtobufContentComponent } from './components/protobuf-content.component';
158 159
159 export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { 160 export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
160 return markedOptionsService; 161 return markedOptionsService;
@@ -268,7 +269,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) @@ -268,7 +269,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
268 OtaPackageAutocompleteComponent, 269 OtaPackageAutocompleteComponent,
269 WidgetsBundleSearchComponent, 270 WidgetsBundleSearchComponent,
270 CopyButtonComponent, 271 CopyButtonComponent,
271 - TogglePasswordComponent 272 + TogglePasswordComponent,
  273 + ProtobufContentComponent
272 ], 274 ],
273 imports: [ 275 imports: [
274 CommonModule, 276 CommonModule,
@@ -458,7 +460,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) @@ -458,7 +460,8 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
458 OtaPackageAutocompleteComponent, 460 OtaPackageAutocompleteComponent,
459 WidgetsBundleSearchComponent, 461 WidgetsBundleSearchComponent,
460 CopyButtonComponent, 462 CopyButtonComponent,
461 - TogglePasswordComponent 463 + TogglePasswordComponent,
  464 + ProtobufContentComponent
462 ] 465 ]
463 }) 466 })
464 export class SharedModule { } 467 export class SharedModule { }