@c8y/tutorial 1023.68.7 → 1023.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@c8y/tutorial",
3
- "version": "1023.68.7",
3
+ "version": "1023.71.1",
4
4
  "description": "This package is used to scaffold a tutorial for Cumulocity IoT Web SDK which explains a lot of concepts.",
5
5
  "dependencies": {
6
- "@c8y/style": "1023.68.7",
7
- "@c8y/ngx-components": "1023.68.7",
8
- "@c8y/client": "1023.68.7",
9
- "@c8y/bootstrap": "1023.68.7",
6
+ "@c8y/style": "1023.71.1",
7
+ "@c8y/ngx-components": "1023.71.1",
8
+ "@c8y/client": "1023.71.1",
9
+ "@c8y/bootstrap": "1023.71.1",
10
10
  "@angular/cdk": "^20.2.14",
11
11
  "monaco-editor": "~0.53.0",
12
12
  "ngx-bootstrap": "20.0.2",
@@ -14,8 +14,8 @@
14
14
  "rxjs": "7.8.2"
15
15
  },
16
16
  "devDependencies": {
17
- "@c8y/options": "1023.68.7",
18
- "@c8y/devkit": "1023.68.7"
17
+ "@c8y/options": "1023.71.1",
18
+ "@c8y/devkit": "1023.71.1"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "@angular/common": ">=20 <21"
@@ -1,15 +1,15 @@
1
1
  <c8y-title>Chat component example</c8y-title>
2
- <div class="row d-flex-sm">
3
- <div class="col-md-4 p-0">
2
+ <div class="row d-flex-md">
3
+ <div class="col-md-4 col-xs-12 p-0">
4
4
  <div class="d-col p-16 form-group-sm bg-level-1">
5
- <p class="text-medium p-b-16">Config options</p>
5
+ <p class="text-medium p-b-16">ChatConfig options</p>
6
6
 
7
- <label>title</label>
7
+ <label>headline</label>
8
8
  <div class="input-group input-group-sm m-b-8">
9
9
  <input
10
10
  class="form-control"
11
11
  type="text"
12
- [(ngModel)]="config.title"
12
+ [(ngModel)]="config.headline"
13
13
  />
14
14
  <div class="input-group-btn">
15
15
  <button
@@ -21,12 +21,12 @@
21
21
  </div>
22
22
  </div>
23
23
 
24
- <label>headline</label>
24
+ <label>title</label>
25
25
  <div class="input-group input-group-sm m-b-8">
26
26
  <input
27
27
  class="form-control"
28
28
  type="text"
29
- [(ngModel)]="config.headline"
29
+ [(ngModel)]="config.title"
30
30
  />
31
31
  <div class="input-group-btn">
32
32
  <button
@@ -55,6 +55,86 @@
55
55
  </div>
56
56
  </div>
57
57
 
58
+ <label>welcomePosition</label>
59
+ <div class="input-group input-group-sm form-group-sm m-b-8">
60
+ <div class="c8y-select-wrapper">
61
+ <select
62
+ class="form-control"
63
+ [(ngModel)]="config.welcomePosition"
64
+ >
65
+ <option value="top">Top</option>
66
+ <option value="center">Center</option>
67
+ <option value="bottom">Bottom</option>
68
+ </select>
69
+ </div>
70
+ <div class="input-group-btn">
71
+ <button
72
+ class="btn btn-default"
73
+ (click)="reloadComponent()"
74
+ >
75
+ {{ 'Apply' | translate }}
76
+ </button>
77
+ </div>
78
+ </div>
79
+
80
+ <label>suggestionsLayout</label>
81
+ <div class="input-group input-group-sm m-b-8">
82
+ <div class="c8y-select-wrapper">
83
+ <select
84
+ class="form-control"
85
+ [(ngModel)]="config.suggestionsLayout"
86
+ >
87
+ <option value="vertical">Vertical</option>
88
+ <option value="horizontal">Horizontal</option>
89
+ </select>
90
+ </div>
91
+ <div class="input-group-btn">
92
+ <button
93
+ class="btn btn-default"
94
+ (click)="reloadComponent()"
95
+ >
96
+ {{ 'Apply' | translate }}
97
+ </button>
98
+ </div>
99
+ </div>
100
+
101
+ <label>scrollbarOnlyOnHover</label>
102
+ <div class="m-b-8">
103
+ <label
104
+ class="c8y-switch"
105
+ title="scrollbarOnlyOnHover"
106
+ >
107
+ <input
108
+ type="checkbox"
109
+ [(ngModel)]="config.scrollbarOnlyOnHover"
110
+ (ngModelChange)="reloadComponent()"
111
+ />
112
+ <span></span>
113
+ <span>scrollbarOnlyOnHover</span>
114
+ </label>
115
+ </div>
116
+
117
+ <label>appearance</label>
118
+ <div class="input-group input-group-sm m-b-8">
119
+ <div class="c8y-select-wrapper">
120
+ <select
121
+ class="form-control"
122
+ [(ngModel)]="config.appearance"
123
+ >
124
+ <option value="framed">framed</option>
125
+ <option value="flat">flat</option>
126
+ </select>
127
+ </div>
128
+ <div class="input-group-btn">
129
+ <button
130
+ class="btn btn-default"
131
+ (click)="reloadComponent()"
132
+ >
133
+ {{ 'Apply' | translate }}
134
+ </button>
135
+ </div>
136
+ </div>
137
+
58
138
  <label>placeholder</label>
59
139
  <div class="input-group input-group-sm m-b-8">
60
140
  <input
@@ -72,12 +152,12 @@
72
152
  </div>
73
153
  </div>
74
154
 
75
- <label>sendButtonText (displayed as a tooltip)</label>
155
+ <label>disclaimerText</label>
76
156
  <div class="input-group input-group-sm m-b-8">
77
157
  <input
78
158
  class="form-control"
79
159
  type="text"
80
- [(ngModel)]="config.sendButtonText"
160
+ [(ngModel)]="config.disclaimerText"
81
161
  />
82
162
  <div class="input-group-btn">
83
163
  <button
@@ -89,12 +169,12 @@
89
169
  </div>
90
170
  </div>
91
171
 
92
- <label>cancelButtonText (displayed as a tooltip)</label>
172
+ <label>sendButtonText (displayed as a tooltip)</label>
93
173
  <div class="input-group input-group-sm m-b-8">
94
174
  <input
95
175
  class="form-control"
96
176
  type="text"
97
- [(ngModel)]="config.cancelButtonText"
177
+ [(ngModel)]="config.sendButtonText"
98
178
  />
99
179
  <div class="input-group-btn">
100
180
  <button
@@ -106,12 +186,12 @@
106
186
  </div>
107
187
  </div>
108
188
 
109
- <label>disclaimerText</label>
189
+ <label>cancelButtonText (displayed as a tooltip)</label>
110
190
  <div class="input-group input-group-sm m-b-8">
111
191
  <input
112
192
  class="form-control"
113
193
  type="text"
114
- [(ngModel)]="config.disclaimerText"
194
+ [(ngModel)]="config.cancelButtonText"
115
195
  />
116
196
  <div class="input-group-btn">
117
197
  <button
@@ -126,7 +206,7 @@
126
206
  <pre class="m-t-16 inner-scroll small">{{ config | json }}</pre>
127
207
  </div>
128
208
  </div>
129
- <div class="col-md-8">
209
+ <div class="col-md-8 col-xs-12">
130
210
  @if (showComponent) {
131
211
  <label
132
212
  class="c8y-switch m-t-8"
@@ -143,12 +223,13 @@
143
223
  </label>
144
224
  <div
145
225
  class="card m-t-16"
146
- style="height: 530px"
226
+ style="height: 730px"
147
227
  >
148
228
  <c8y-ai-chat
149
229
  [config]="config"
150
230
  (onMessage)="sendMessage($event)"
151
231
  [isLoading]="isLoading"
232
+ [suggestionsTemplate]="suggestionsRef"
152
233
  >
153
234
  @if (showInitialMessage) {
154
235
  <c8y-ai-chat-message>
@@ -163,8 +244,24 @@
163
244
  ></c8y-ai-chat-message-action>
164
245
  </c8y-ai-chat-message>
165
246
  }
166
- @for (message of displayMessages; track message) {
247
+ @for (message of displayMessages; track message; let i = $index) {
167
248
  <c8y-ai-chat-message [message]="message">
249
+ @if (message.role === 'user') {
250
+ <div [innerHTML]="message.content | markdownToHtml | async"></div>
251
+ } @else {
252
+ <ng-container
253
+ [ngComponentOutlet]="AiChatAssistantMessageComponent"
254
+ [ngComponentOutletInputs]="{
255
+ assistantMessageContext: {
256
+ message: message,
257
+ config: assistantMessageDisplayConfig,
258
+ isMessageLoading: isLoading && i === messages.length - 1,
259
+ messageDisplayIndex: messages.length - 1 - i
260
+ }
261
+ }"
262
+ ></ng-container>
263
+ }
264
+
168
265
  @if (message.role === 'assistant') {
169
266
  <c8y-ai-chat-message-action
170
267
  icon="thumbs-up"
@@ -183,7 +280,7 @@
183
280
  <c8y-ai-chat-message-action
184
281
  icon="undo"
185
282
  tooltip="Revert"
186
- (click)="revertMessage(message)"
283
+ (actionClicked)="revertMessage(message)"
187
284
  ></c8y-ai-chat-message-action>
188
285
  }
189
286
 
@@ -208,6 +305,21 @@
208
305
  </c8y-ai-chat-message>
209
306
  }
210
307
 
308
+ <div slot="welcome">
309
+ You can also project custom content into the welcome area of the chat component.
310
+ </div>
311
+ @if (showInitialMessage) {
312
+ <div slot="before-messages">
313
+ You can also project custom content before all messages.
314
+ </div>
315
+ }
316
+ @if (showInitialMessage) {
317
+ <div slot="after-messages">You can also project custom content after all messages.</div>
318
+ }
319
+ @if (showInitialMessage) {
320
+ <div>This is content that is projected by default and always shown.</div>
321
+ }
322
+
211
323
  <c8y-ai-chat-suggestion
212
324
  [label]="'Create device '"
213
325
  [prompt]="'AHOI, create a new device'"
@@ -221,6 +333,17 @@
221
333
  [prompt]="'Create a device with a measurement and an event'"
222
334
  (suggestionClicked)="sendMessage($event)"
223
335
  ></c8y-ai-chat-suggestion>
336
+
337
+ <!-- For cases where dynamic behaviour is needed, use `<ng-template>` -->
338
+ <ng-template #suggestionsRef>
339
+ <c8y-ai-chat-suggestion
340
+ [icon]="'c8y-bulb'"
341
+ [useAiButtons]="true"
342
+ [label]="'Dynamic suggestion from AI'"
343
+ [prompt]="'Dynamic suggestion from AI'"
344
+ (suggestionClicked)="sendMessage($event)"
345
+ ></c8y-ai-chat-suggestion>
346
+ </ng-template>
224
347
  </c8y-ai-chat>
225
348
  </div>
226
349
  }
@@ -1,14 +1,16 @@
1
- import { JsonPipe } from '@angular/common';
1
+ import { JsonPipe, NgComponentOutlet } from '@angular/common';
2
2
  import { Component } from '@angular/core';
3
3
  import { FormsModule } from '@angular/forms';
4
4
  import { CoreModule, LoadingComponent } from '@c8y/ngx-components';
5
- import { AIMessage } from '@c8y/ngx-components/ai';
5
+ import { AIMessage, AssistantMessageDisplayConfig, ChatConfig } from '@c8y/ngx-components/ai';
6
6
  import {
7
+ AiChatAssistantMessageComponent,
7
8
  AiChatComponent,
8
9
  AiChatMessageActionComponent,
9
10
  AiChatMessageComponent,
10
11
  AiChatSuggestionComponent
11
12
  } from '@c8y/ngx-components/ai/ai-chat';
13
+ import { gettext } from '@c8y/ngx-components/gettext';
12
14
 
13
15
  @Component({
14
16
  selector: 'c8y-ai-chat-example',
@@ -20,12 +22,15 @@ import {
20
22
  AiChatMessageActionComponent,
21
23
  AiChatSuggestionComponent,
22
24
  LoadingComponent,
25
+ NgComponentOutlet,
23
26
  CoreModule,
24
27
  FormsModule,
25
28
  JsonPipe
26
29
  ]
27
30
  })
28
31
  export class ChatExampleComponent {
32
+ AiChatAssistantMessageComponent = AiChatAssistantMessageComponent;
33
+
29
34
  private _showTimestamps = false;
30
35
  showInitialMessage = false;
31
36
 
@@ -38,16 +43,27 @@ export class ChatExampleComponent {
38
43
  this.updateDisplayMessages();
39
44
  }
40
45
 
41
- config = {
46
+ config: ChatConfig = {
42
47
  headline: 'Welcome!',
43
48
  welcomeText: 'Type a message below to get started.',
49
+ welcomePosition: 'top',
44
50
  title: 'What can I help you with?',
45
51
  placeholder: 'Type your message here...',
46
52
  sendButtonText: 'Send',
47
53
  cancelButtonText: 'Cancel',
48
54
  disclaimerText: 'AI-generated responses can contain errors. Verify the details before use.',
55
+ suggestionsLayout: 'horizontal',
56
+ appearance: 'framed',
49
57
  userInterfaceIcons: { send: 'arrow-circle-up', cancel: 'stop-circle' }
50
58
  };
59
+ assistantMessageDisplayConfig: AssistantMessageDisplayConfig = {
60
+ toolCallConfig: {
61
+ 'c8y-read-documentation': {
62
+ executingLabel: gettext('Reading Cumulocity documentation'),
63
+ completedLabel: gettext('Read Cumulocity documentation')
64
+ }
65
+ }
66
+ };
51
67
 
52
68
  messages: AIMessage[] = [];
53
69
  displayMessages: AIMessage[] = [];
@@ -62,7 +78,43 @@ export class ChatExampleComponent {
62
78
  setTimeout(() => {
63
79
  // Simulate a delay for the response
64
80
  this.messages.push({
65
- content: 'Thank you for your message!',
81
+ content: "I've finished implementing your **amazing idea**.",
82
+ steps: [
83
+ {
84
+ type: 'text',
85
+ text: '**Great idea**, I can definitely help you with that!\n\nFirst let me use some tools to gather the information I need.',
86
+ reasoning:
87
+ "The user asked me a question but I need more information. I see, I have some tools available that can help! \n\nNow I'll initiate a call to help solve this problem.",
88
+ toolResults: [
89
+ {
90
+ toolName: 'c8y-read-documentation',
91
+ toolCallId: 'call1',
92
+ type: 'tool-result',
93
+ input: { query: 'Example query' },
94
+ output: '<html>\n<h1>\n C8Y doc sample\n</h1>\n</html>'
95
+ },
96
+ {
97
+ toolName: 'c8y-tool-with-json-output',
98
+ toolCallId: 'call3',
99
+ type: 'tool-result',
100
+ input: {},
101
+ output: { 'Key 1': 123, 'Key 2': 'Value 2' }
102
+ },
103
+ {
104
+ toolName: 'c8y-tool-with-error',
105
+ toolCallId: 'call4',
106
+ type: 'tool-result',
107
+ input: { url: 'something invalid' },
108
+ output: 'This is an error message\nSomething bad happened',
109
+ error: true
110
+ }
111
+ ]
112
+ },
113
+ {
114
+ type: 'text',
115
+ text: 'Your **excellent idea** has now been implemented!\n\nWhat would you like me to help with next?'
116
+ }
117
+ ],
66
118
  role: 'assistant',
67
119
  timestamp: new Date().toISOString()
68
120
  });