@things-factory/ai-dashboard 0.0.0-stage → 10.1.213

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.
Files changed (108) hide show
  1. package/client/bootstrap.ts +25 -0
  2. package/client/components/card-dialogs.ts +422 -0
  3. package/client/components/card-library.ts +272 -0
  4. package/client/components/dashboard-card.ts +450 -0
  5. package/client/components/proposal-preview.ts +96 -0
  6. package/client/index.ts +3 -0
  7. package/client/pages/my-dashboard-page.ts +371 -0
  8. package/client/route.ts +8 -0
  9. package/client/tsconfig.json +13 -0
  10. package/client/utils/card-api.ts +110 -0
  11. package/client/utils/card-words.ts +80 -0
  12. package/dist-client/bootstrap.d.ts +2 -0
  13. package/dist-client/bootstrap.js +24 -0
  14. package/dist-client/bootstrap.js.map +1 -0
  15. package/dist-client/components/card-dialogs.d.ts +59 -0
  16. package/dist-client/components/card-dialogs.js +457 -0
  17. package/dist-client/components/card-dialogs.js.map +1 -0
  18. package/dist-client/components/card-library.d.ts +23 -0
  19. package/dist-client/components/card-library.js +276 -0
  20. package/dist-client/components/card-library.js.map +1 -0
  21. package/dist-client/components/dashboard-card.d.ts +35 -0
  22. package/dist-client/components/dashboard-card.js +490 -0
  23. package/dist-client/components/dashboard-card.js.map +1 -0
  24. package/dist-client/components/proposal-preview.d.ts +17 -0
  25. package/dist-client/components/proposal-preview.js +111 -0
  26. package/dist-client/components/proposal-preview.js.map +1 -0
  27. package/dist-client/index.d.ts +3 -0
  28. package/dist-client/index.js +4 -0
  29. package/dist-client/index.js.map +1 -0
  30. package/dist-client/pages/my-dashboard-page.d.ts +46 -0
  31. package/dist-client/pages/my-dashboard-page.js +376 -0
  32. package/dist-client/pages/my-dashboard-page.js.map +1 -0
  33. package/dist-client/route.d.ts +1 -0
  34. package/dist-client/route.js +9 -0
  35. package/dist-client/route.js.map +1 -0
  36. package/dist-client/tsconfig.tsbuildinfo +1 -0
  37. package/dist-client/utils/card-api.d.ts +95 -0
  38. package/dist-client/utils/card-api.js +39 -0
  39. package/dist-client/utils/card-api.js.map +1 -0
  40. package/dist-client/utils/card-words.d.ts +24 -0
  41. package/dist-client/utils/card-words.js +72 -0
  42. package/dist-client/utils/card-words.js.map +1 -0
  43. package/dist-server/index.d.ts +11 -0
  44. package/dist-server/index.js +19 -0
  45. package/dist-server/index.js.map +1 -0
  46. package/dist-server/role-tasks.d.ts +2 -0
  47. package/dist-server/role-tasks.js +13 -0
  48. package/dist-server/role-tasks.js.map +1 -0
  49. package/dist-server/service/card/card-access.d.ts +32 -0
  50. package/dist-server/service/card/card-access.js +45 -0
  51. package/dist-server/service/card/card-access.js.map +1 -0
  52. package/dist-server/service/card/card-definition.d.ts +100 -0
  53. package/dist-server/service/card/card-definition.js +237 -0
  54. package/dist-server/service/card/card-definition.js.map +1 -0
  55. package/dist-server/service/card/card-names.d.ts +5 -0
  56. package/dist-server/service/card/card-names.js +9 -0
  57. package/dist-server/service/card/card-names.js.map +1 -0
  58. package/dist-server/service/card/card-proposal.d.ts +21 -0
  59. package/dist-server/service/card/card-proposal.js +28 -0
  60. package/dist-server/service/card/card-proposal.js.map +1 -0
  61. package/dist-server/service/card/card-resolver.d.ts +29 -0
  62. package/dist-server/service/card/card-resolver.js +541 -0
  63. package/dist-server/service/card/card-resolver.js.map +1 -0
  64. package/dist-server/service/card/card-run.d.ts +48 -0
  65. package/dist-server/service/card/card-run.js +239 -0
  66. package/dist-server/service/card/card-run.js.map +1 -0
  67. package/dist-server/service/card/card-tool.d.ts +218 -0
  68. package/dist-server/service/card/card-tool.js +313 -0
  69. package/dist-server/service/card/card-tool.js.map +1 -0
  70. package/dist-server/service/card/card-types.d.ts +56 -0
  71. package/dist-server/service/card/card-types.js +212 -0
  72. package/dist-server/service/card/card-types.js.map +1 -0
  73. package/dist-server/service/card/dashboard-card.d.ts +87 -0
  74. package/dist-server/service/card/dashboard-card.js +388 -0
  75. package/dist-server/service/card/dashboard-card.js.map +1 -0
  76. package/dist-server/service/card/long-text.d.ts +6 -0
  77. package/dist-server/service/card/long-text.js +28 -0
  78. package/dist-server/service/card/long-text.js.map +1 -0
  79. package/dist-server/service/index.d.ts +12 -0
  80. package/dist-server/service/index.js +24 -0
  81. package/dist-server/service/index.js.map +1 -0
  82. package/dist-server/tsconfig.tsbuildinfo +1 -0
  83. package/package.json +47 -4
  84. package/server/index.ts +16 -0
  85. package/server/role-tasks.ts +12 -0
  86. package/server/service/card/card-access.test.ts +138 -0
  87. package/server/service/card/card-access.ts +53 -0
  88. package/server/service/card/card-definition.ts +298 -0
  89. package/server/service/card/card-names.ts +5 -0
  90. package/server/service/card/card-proposal.ts +37 -0
  91. package/server/service/card/card-resolver.ts +434 -0
  92. package/server/service/card/card-run.test.ts +237 -0
  93. package/server/service/card/card-run.ts +288 -0
  94. package/server/service/card/card-tool.ts +299 -0
  95. package/server/service/card/card-types.ts +145 -0
  96. package/server/service/card/dashboard-card.ts +301 -0
  97. package/server/service/card/long-text.ts +25 -0
  98. package/server/service/index.ts +16 -0
  99. package/test/translations.test.ts +42 -0
  100. package/things-factory.config.js +8 -0
  101. package/translations/en.json +126 -0
  102. package/translations/es.json +126 -0
  103. package/translations/ja.json +126 -0
  104. package/translations/ko.json +126 -0
  105. package/translations/ms.json +126 -0
  106. package/translations/zh.json +126 -0
  107. package/tsconfig.json +9 -0
  108. package/README.md +0 -3
@@ -0,0 +1,490 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ /*
3
+ * One card on the dashboard - its name and what it reads, the block its definition draws as the viewer, when it was
4
+ * read and how much of its source it was drawn from (ADR-0160 decisions 4-6, 8). Drawing it asks the server to run
5
+ * the definition; no model is asked.
6
+ */
7
+ import '@material/web/icon/icon.js';
8
+ import '@things-factory/ai-assistant/dist-client/index.js';
9
+ import { LitElement, css, html, nothing } from 'lit';
10
+ import { customElement, property, state } from 'lit/decorators.js';
11
+ import { i18next, localize } from '@operato/i18n';
12
+ import { cardApi } from '../utils/card-api.js';
13
+ import { drawsNothing, issueLine, ownershipLine, partialLine, readLine, viewIcon } from '../utils/card-words.js';
14
+ const t = (key, options = {}) => String(i18next.t(key, options));
15
+ let AiDashboardCard = class AiDashboardCard extends localize(i18next)(LitElement) {
16
+ constructor() {
17
+ super(...arguments);
18
+ /** The place's size, when it is on the dashboard. */
19
+ this.width = 2;
20
+ this.height = 1;
21
+ this.editing = false;
22
+ this.first = false;
23
+ this.last = false;
24
+ this.loading = false;
25
+ this.menu = false;
26
+ this.closeMenu = (event) => {
27
+ if (!event.composedPath().includes(this))
28
+ this.menu = false;
29
+ };
30
+ }
31
+ static { this.styles = css `
32
+ :host {
33
+ display: block;
34
+ min-width: 0;
35
+ min-height: 0;
36
+ }
37
+
38
+ article {
39
+ position: relative;
40
+ display: flex;
41
+ flex-direction: column;
42
+ height: 100%;
43
+ box-sizing: border-box;
44
+ border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
45
+ border-radius: var(--ui-r, 12px);
46
+ background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));
47
+ overflow: hidden;
48
+ transition:
49
+ box-shadow 0.18s ease,
50
+ border-color 0.18s ease,
51
+ transform 0.18s ease;
52
+ }
53
+
54
+ article:hover {
55
+ border-color: color-mix(in srgb, var(--ui-accent, var(--md-sys-color-primary)) 35%, var(--ui-line, transparent));
56
+ box-shadow: 0 6px 20px -12px rgb(0 0 0 / 35%);
57
+ }
58
+
59
+ :host([editing]) article {
60
+ border-style: dashed;
61
+ border-color: var(--ui-accent, var(--md-sys-color-primary));
62
+ }
63
+
64
+ header {
65
+ display: flex;
66
+ align-items: flex-start;
67
+ gap: 10px;
68
+ padding: 14px 14px 6px 16px;
69
+ }
70
+
71
+ .kind {
72
+ flex: none;
73
+ display: grid;
74
+ place-items: center;
75
+ width: 30px;
76
+ height: 30px;
77
+ border-radius: 8px;
78
+ background: var(--ui-accent-wash, color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent));
79
+ color: var(--ui-accent, var(--md-sys-color-primary));
80
+ --md-icon-size: 18px;
81
+ }
82
+
83
+ .titles {
84
+ flex: 1;
85
+ min-width: 0;
86
+ }
87
+
88
+ h4 {
89
+ margin: 0;
90
+ font-size: var(--ui-t-title, 15px);
91
+ font-weight: 600;
92
+ line-height: 1.3;
93
+ color: var(--ui-ink, var(--md-sys-color-on-surface));
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ }
98
+
99
+ .sub {
100
+ display: block;
101
+ margin-top: 2px;
102
+ font-size: var(--ui-t-small, 12px);
103
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
104
+ overflow: hidden;
105
+ text-overflow: ellipsis;
106
+ white-space: nowrap;
107
+ }
108
+
109
+ .tools {
110
+ flex: none;
111
+ display: flex;
112
+ gap: 2px;
113
+ opacity: 0.55;
114
+ transition: opacity 0.15s ease;
115
+ }
116
+
117
+ article:hover .tools,
118
+ article:focus-within .tools,
119
+ :host([editing]) .tools {
120
+ opacity: 1;
121
+ }
122
+
123
+ button.icon {
124
+ display: grid;
125
+ place-items: center;
126
+ width: 30px;
127
+ height: 30px;
128
+ padding: 0;
129
+ border: 0;
130
+ border-radius: 8px;
131
+ background: transparent;
132
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
133
+ cursor: pointer;
134
+ --md-icon-size: 18px;
135
+ }
136
+
137
+ button.icon:hover,
138
+ button.icon:focus-visible {
139
+ background: var(--ui-hover, var(--md-sys-color-surface-container-high));
140
+ color: var(--ui-ink, var(--md-sys-color-on-surface));
141
+ outline: none;
142
+ }
143
+
144
+ button.icon[disabled] {
145
+ opacity: 0.35;
146
+ cursor: default;
147
+ }
148
+
149
+ button.icon.spin md-icon {
150
+ animation: spin 0.9s linear infinite;
151
+ }
152
+
153
+ @keyframes spin {
154
+ to {
155
+ transform: rotate(360deg);
156
+ }
157
+ }
158
+
159
+ .menu {
160
+ position: absolute;
161
+ top: 44px;
162
+ right: 10px;
163
+ z-index: 2;
164
+ display: flex;
165
+ flex-direction: column;
166
+ min-width: 180px;
167
+ padding: 6px;
168
+ border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));
169
+ border-radius: 10px;
170
+ background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));
171
+ box-shadow: 0 12px 32px -12px rgb(0 0 0 / 40%);
172
+ }
173
+
174
+ .menu button {
175
+ display: flex;
176
+ align-items: center;
177
+ gap: 10px;
178
+ padding: 8px 10px;
179
+ border: 0;
180
+ border-radius: 6px;
181
+ background: transparent;
182
+ color: var(--ui-ink, var(--md-sys-color-on-surface));
183
+ font: inherit;
184
+ font-size: var(--ui-t-body, 13px);
185
+ text-align: start;
186
+ cursor: pointer;
187
+ --md-icon-size: 18px;
188
+ }
189
+
190
+ .menu button:hover,
191
+ .menu button:focus-visible {
192
+ background: var(--ui-hover, var(--md-sys-color-surface-container-high));
193
+ outline: none;
194
+ }
195
+
196
+ .menu hr {
197
+ width: 100%;
198
+ margin: 4px 0;
199
+ border: 0;
200
+ border-top: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));
201
+ }
202
+
203
+ .body {
204
+ flex: 1;
205
+ min-height: 0;
206
+ padding: 4px 16px 8px;
207
+ overflow: auto;
208
+ }
209
+
210
+ .skeleton {
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 10px;
214
+ padding-top: 8px;
215
+ }
216
+
217
+ .skeleton span {
218
+ display: block;
219
+ height: 14px;
220
+ border-radius: 6px;
221
+ background: linear-gradient(90deg, var(--ui-line-soft, #eee) 0%, var(--ui-panel-2, #f6f6f6) 50%, var(--ui-line-soft, #eee) 100%);
222
+ background-size: 200% 100%;
223
+ animation: shimmer 1.4s ease-in-out infinite;
224
+ }
225
+
226
+ .skeleton span:nth-child(1) {
227
+ width: 40%;
228
+ height: 28px;
229
+ }
230
+
231
+ .skeleton span:nth-child(2) {
232
+ width: 85%;
233
+ }
234
+
235
+ .skeleton span:nth-child(3) {
236
+ width: 65%;
237
+ }
238
+
239
+ @keyframes shimmer {
240
+ from {
241
+ background-position: 200% 0;
242
+ }
243
+ to {
244
+ background-position: -200% 0;
245
+ }
246
+ }
247
+
248
+ .issue {
249
+ display: flex;
250
+ gap: 10px;
251
+ align-items: flex-start;
252
+ margin-top: 8px;
253
+ padding: 12px;
254
+ border-radius: 8px;
255
+ background: color-mix(in srgb, var(--ui-crit, #c62828) 8%, transparent);
256
+ color: var(--ui-ink, var(--md-sys-color-on-surface));
257
+ font-size: var(--ui-t-body, 13px);
258
+ --md-icon-size: 18px;
259
+ }
260
+
261
+ .issue md-icon {
262
+ flex: none;
263
+ color: var(--ui-crit, #c62828);
264
+ }
265
+
266
+ .issue.access {
267
+ background: var(--ui-panel-2, var(--md-sys-color-surface-container));
268
+ }
269
+
270
+ .issue.access md-icon {
271
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
272
+ }
273
+
274
+ .nothing {
275
+ display: flex;
276
+ flex-direction: column;
277
+ align-items: center;
278
+ justify-content: center;
279
+ gap: 6px;
280
+ height: 100%;
281
+ min-height: 120px;
282
+ color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
283
+ font-size: var(--ui-t-body, 13px);
284
+ text-align: center;
285
+ --md-icon-size: 28px;
286
+ }
287
+
288
+ footer {
289
+ display: flex;
290
+ flex-wrap: wrap;
291
+ align-items: center;
292
+ gap: 4px 10px;
293
+ padding: 6px 16px 12px;
294
+ font-size: var(--ui-t-micro, 11px);
295
+ color: var(--ui-faint, var(--md-sys-color-on-surface-variant));
296
+ }
297
+
298
+ footer .partial {
299
+ display: inline-flex;
300
+ align-items: center;
301
+ gap: 4px;
302
+ color: var(--ui-warn, #b26a00);
303
+ --md-icon-size: 14px;
304
+ }
305
+
306
+ .edit {
307
+ display: flex;
308
+ flex-wrap: wrap;
309
+ align-items: center;
310
+ justify-content: center;
311
+ gap: 4px;
312
+ padding: 8px;
313
+ border-top: 1px dashed var(--ui-line, var(--md-sys-color-outline-variant));
314
+ background: var(--ui-panel-2, var(--md-sys-color-surface-container-low));
315
+ }
316
+
317
+ .edit .size {
318
+ margin: 0 6px;
319
+ font-size: var(--ui-t-small, 12px);
320
+ color: var(--ui-muted, var(--md-sys-color-on-surface-variant));
321
+ font-variant-numeric: tabular-nums;
322
+ }
323
+ `; }
324
+ connectedCallback() {
325
+ super.connectedCallback();
326
+ document.addEventListener('click', this.closeMenu, true);
327
+ }
328
+ disconnectedCallback() {
329
+ document.removeEventListener('click', this.closeMenu, true);
330
+ super.disconnectedCallback();
331
+ }
332
+ updated(changes) {
333
+ /* A new card, or a new version of it, is read again; nothing else is. */
334
+ const key = this.card ? `${this.card.id}@${this.card.version}` : undefined;
335
+ if (changes.has('card') && key && key !== this.readingFor)
336
+ this.refresh();
337
+ }
338
+ /** Draws the card again from the data - the server runs its definition as the viewer. */
339
+ async refresh() {
340
+ if (!this.card)
341
+ return;
342
+ const key = `${this.card.id}@${this.card.version}`;
343
+ this.readingFor = key;
344
+ this.loading = true;
345
+ this.failed = undefined;
346
+ try {
347
+ const run = await cardApi.run(this.card.id);
348
+ if (this.readingFor !== key)
349
+ return;
350
+ this.run = run;
351
+ this.readAt = new Date();
352
+ }
353
+ catch (error) {
354
+ if (this.readingFor === key)
355
+ this.failed = error?.message ?? String(error);
356
+ }
357
+ finally {
358
+ if (this.readingFor === key)
359
+ this.loading = false;
360
+ }
361
+ }
362
+ act(action) {
363
+ this.menu = false;
364
+ if (action === 'refresh')
365
+ return this.refresh();
366
+ this.dispatchEvent(new CustomEvent('card-action', { detail: { action, card: this.card }, bubbles: true, composed: true }));
367
+ }
368
+ render() {
369
+ const card = this.card;
370
+ if (!card)
371
+ return nothing;
372
+ const owner = card.access === 'owner';
373
+ const source = this.run?.source?.labelKey ? t(this.run.source.labelKey) : '';
374
+ const ownership = ownershipLine(card, t);
375
+ const sub = [card.description || source, ownership].filter(Boolean).join(' · ');
376
+ return html `<article aria-busy=${this.loading ? 'true' : 'false'}>
377
+ <header>
378
+ <span class="kind"><md-icon>${viewIcon(card.definition)}</md-icon></span>
379
+ <div class="titles">
380
+ <h4 title=${card.name}>${card.name}</h4>
381
+ ${sub ? html `<span class="sub" title=${sub}>${sub}</span>` : nothing}
382
+ </div>
383
+ <div class="tools">
384
+ <button class="icon ${this.loading ? 'spin' : ''}" title=${t('ai-dashboard.card.refresh')} aria-label=${t('ai-dashboard.card.refresh')} ?disabled=${this.loading} @click=${() => this.act('refresh')}>
385
+ <md-icon>refresh</md-icon>
386
+ </button>
387
+ ${owner
388
+ ? html `<button class="icon" title=${t('ai-dashboard.card.ai')} aria-label=${t('ai-dashboard.card.ai')} @click=${() => this.act('ai')}><md-icon>auto_awesome</md-icon></button>`
389
+ : nothing}
390
+ <button class="icon" title=${t('ai-dashboard.card.more')} aria-label=${t('ai-dashboard.card.more')} aria-expanded=${this.menu ? 'true' : 'false'} @click=${() => (this.menu = !this.menu)}>
391
+ <md-icon>more_vert</md-icon>
392
+ </button>
393
+ </div>
394
+ </header>
395
+ ${this.menu ? this.renderMenu(owner) : nothing}
396
+ <div class="body">${this.renderBody()}</div>
397
+ ${this.run && !this.run.issue ? this.renderFooter() : nothing}
398
+ ${this.editing ? this.renderEdit() : nothing}
399
+ </article>`;
400
+ }
401
+ renderMenu(owner) {
402
+ const item = (action, icon, key) => html `<button role="menuitem" @click=${() => this.act(action)}><md-icon>${icon}</md-icon>${t(key)}</button>`;
403
+ return html `<div class="menu" role="menu">
404
+ ${owner ? item('ai', 'auto_awesome', 'ai-dashboard.card.ai') : nothing} ${owner ? item('rename', 'edit', 'ai-dashboard.card.rename') : nothing}
405
+ ${owner ? item('share', 'share', 'ai-dashboard.card.share') : nothing} ${owner ? item('versions', 'history', 'ai-dashboard.card.versions') : nothing}
406
+ ${item('copy', 'content_copy', owner ? 'ai-dashboard.card.duplicate' : 'ai-dashboard.card.copy')}
407
+ <hr />
408
+ ${item('unplace', 'remove_circle_outline', 'ai-dashboard.card.unplace')}
409
+ </div>`;
410
+ }
411
+ renderBody() {
412
+ if (this.failed)
413
+ return html `<div class="issue"><md-icon>error</md-icon><span>${this.failed}</span></div>`;
414
+ if (!this.run)
415
+ return html `<div class="skeleton" aria-hidden="true"><span></span><span></span><span></span></div>`;
416
+ const issue = issueLine(this.run, t);
417
+ if (issue)
418
+ return html `<div class="issue ${issue.access ? 'access' : ''}"><md-icon>${issue.access ? 'lock' : 'error'}</md-icon><span>${issue.text}</span></div>`;
419
+ if (drawsNothing(this.run))
420
+ return html `<div class="nothing"><md-icon>inbox</md-icon><span>${t('ai-dashboard.card.nothing')}</span></div>`;
421
+ return html `<ox-assistant-workspace-view compact .workspace=${{ name: this.card.id, title: this.card.name, blocks: [this.run.block] }}></ox-assistant-workspace-view>`;
422
+ }
423
+ renderFooter() {
424
+ const partial = partialLine(this.run, t);
425
+ return html `<footer>
426
+ <span>${readLine(this.run, this.readAt ?? new Date(), t, i18next.language)}</span>
427
+ ${partial ? html `<span class="partial"><md-icon>warning</md-icon>${partial}</span>` : nothing}
428
+ </footer>`;
429
+ }
430
+ renderEdit() {
431
+ const button = (action, icon, key, disabled = false) => html `<button class="icon" title=${t(key)} aria-label=${t(key)} ?disabled=${disabled} @click=${() => this.act(action)}><md-icon>${icon}</md-icon></button>`;
432
+ return html `<div class="edit">
433
+ ${button('left', 'arrow_back', 'ai-dashboard.edit.left', this.first)} ${button('right', 'arrow_forward', 'ai-dashboard.edit.right', this.last)}
434
+ ${button('narrower', 'remove', 'ai-dashboard.edit.narrower', this.width <= 1)}
435
+ <span class="size">${this.width} × ${this.height}</span>
436
+ ${button('wider', 'add', 'ai-dashboard.edit.wider', this.width >= 4)}
437
+ ${button(this.height > 1 ? 'shorter' : 'taller', this.height > 1 ? 'unfold_less' : 'unfold_more', this.height > 1 ? 'ai-dashboard.edit.shorter' : 'ai-dashboard.edit.taller')}
438
+ ${button('unplace', 'delete_outline', 'ai-dashboard.card.unplace')}
439
+ </div>`;
440
+ }
441
+ };
442
+ __decorate([
443
+ property({ attribute: false }),
444
+ __metadata("design:type", Object)
445
+ ], AiDashboardCard.prototype, "card", void 0);
446
+ __decorate([
447
+ property({ type: Number }),
448
+ __metadata("design:type", Object)
449
+ ], AiDashboardCard.prototype, "width", void 0);
450
+ __decorate([
451
+ property({ type: Number }),
452
+ __metadata("design:type", Object)
453
+ ], AiDashboardCard.prototype, "height", void 0);
454
+ __decorate([
455
+ property({ type: Boolean, reflect: true }),
456
+ __metadata("design:type", Object)
457
+ ], AiDashboardCard.prototype, "editing", void 0);
458
+ __decorate([
459
+ property({ type: Boolean }),
460
+ __metadata("design:type", Object)
461
+ ], AiDashboardCard.prototype, "first", void 0);
462
+ __decorate([
463
+ property({ type: Boolean }),
464
+ __metadata("design:type", Object)
465
+ ], AiDashboardCard.prototype, "last", void 0);
466
+ __decorate([
467
+ state(),
468
+ __metadata("design:type", Object)
469
+ ], AiDashboardCard.prototype, "run", void 0);
470
+ __decorate([
471
+ state(),
472
+ __metadata("design:type", String)
473
+ ], AiDashboardCard.prototype, "failed", void 0);
474
+ __decorate([
475
+ state(),
476
+ __metadata("design:type", Object)
477
+ ], AiDashboardCard.prototype, "loading", void 0);
478
+ __decorate([
479
+ state(),
480
+ __metadata("design:type", Object)
481
+ ], AiDashboardCard.prototype, "menu", void 0);
482
+ __decorate([
483
+ state(),
484
+ __metadata("design:type", Date)
485
+ ], AiDashboardCard.prototype, "readAt", void 0);
486
+ AiDashboardCard = __decorate([
487
+ customElement('ai-dashboard-card')
488
+ ], AiDashboardCard);
489
+ export { AiDashboardCard };
490
+ //# sourceMappingURL=dashboard-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-card.js","sourceRoot":"","sources":["../../client/components/dashboard-card.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * One card on the dashboard - its name and what it reads, the block its definition draws as the viewer, when it was\n * read and how much of its source it was drawn from (ADR-0160 decisions 4-6, 8). Drawing it asks the server to run\n * the definition; no model is asked.\n */\nimport '@material/web/icon/icon.js'\nimport '@things-factory/ai-assistant/dist-client/index.js'\n\nimport { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\n\nimport { cardApi, type CardItem, type CardRun } from '../utils/card-api.js'\nimport { drawsNothing, issueLine, ownershipLine, partialLine, readLine, viewIcon } from '../utils/card-words.js'\n\nconst t = (key: string, options: Record<string, unknown> = {}) => String(i18next.t(key, options))\n\nexport type CardAction = 'refresh' | 'ai' | 'rename' | 'share' | 'versions' | 'copy' | 'unplace' | 'left' | 'right' | 'wider' | 'narrower' | 'taller' | 'shorter'\n\n@customElement('ai-dashboard-card')\nexport class AiDashboardCard extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n display: block;\n min-width: 0;\n min-height: 0;\n }\n\n article {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n box-sizing: border-box;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r, 12px);\n background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));\n overflow: hidden;\n transition:\n box-shadow 0.18s ease,\n border-color 0.18s ease,\n transform 0.18s ease;\n }\n\n article:hover {\n border-color: color-mix(in srgb, var(--ui-accent, var(--md-sys-color-primary)) 35%, var(--ui-line, transparent));\n box-shadow: 0 6px 20px -12px rgb(0 0 0 / 35%);\n }\n\n :host([editing]) article {\n border-style: dashed;\n border-color: var(--ui-accent, var(--md-sys-color-primary));\n }\n\n header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 14px 14px 6px 16px;\n }\n\n .kind {\n flex: none;\n display: grid;\n place-items: center;\n width: 30px;\n height: 30px;\n border-radius: 8px;\n background: var(--ui-accent-wash, color-mix(in srgb, var(--md-sys-color-primary) 12%, transparent));\n color: var(--ui-accent, var(--md-sys-color-primary));\n --md-icon-size: 18px;\n }\n\n .titles {\n flex: 1;\n min-width: 0;\n }\n\n h4 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n line-height: 1.3;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sub {\n display: block;\n margin-top: 2px;\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .tools {\n flex: none;\n display: flex;\n gap: 2px;\n opacity: 0.55;\n transition: opacity 0.15s ease;\n }\n\n article:hover .tools,\n article:focus-within .tools,\n :host([editing]) .tools {\n opacity: 1;\n }\n\n button.icon {\n display: grid;\n place-items: center;\n width: 30px;\n height: 30px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n cursor: pointer;\n --md-icon-size: 18px;\n }\n\n button.icon:hover,\n button.icon:focus-visible {\n background: var(--ui-hover, var(--md-sys-color-surface-container-high));\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n outline: none;\n }\n\n button.icon[disabled] {\n opacity: 0.35;\n cursor: default;\n }\n\n button.icon.spin md-icon {\n animation: spin 0.9s linear infinite;\n }\n\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n\n .menu {\n position: absolute;\n top: 44px;\n right: 10px;\n z-index: 2;\n display: flex;\n flex-direction: column;\n min-width: 180px;\n padding: 6px;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: 10px;\n background: var(--ui-panel, var(--md-sys-color-surface-container-lowest));\n box-shadow: 0 12px 32px -12px rgb(0 0 0 / 40%);\n }\n\n .menu button {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border: 0;\n border-radius: 6px;\n background: transparent;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n text-align: start;\n cursor: pointer;\n --md-icon-size: 18px;\n }\n\n .menu button:hover,\n .menu button:focus-visible {\n background: var(--ui-hover, var(--md-sys-color-surface-container-high));\n outline: none;\n }\n\n .menu hr {\n width: 100%;\n margin: 4px 0;\n border: 0;\n border-top: 1px solid var(--ui-line-soft, var(--md-sys-color-outline-variant));\n }\n\n .body {\n flex: 1;\n min-height: 0;\n padding: 4px 16px 8px;\n overflow: auto;\n }\n\n .skeleton {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 8px;\n }\n\n .skeleton span {\n display: block;\n height: 14px;\n border-radius: 6px;\n background: linear-gradient(90deg, var(--ui-line-soft, #eee) 0%, var(--ui-panel-2, #f6f6f6) 50%, var(--ui-line-soft, #eee) 100%);\n background-size: 200% 100%;\n animation: shimmer 1.4s ease-in-out infinite;\n }\n\n .skeleton span:nth-child(1) {\n width: 40%;\n height: 28px;\n }\n\n .skeleton span:nth-child(2) {\n width: 85%;\n }\n\n .skeleton span:nth-child(3) {\n width: 65%;\n }\n\n @keyframes shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n\n .issue {\n display: flex;\n gap: 10px;\n align-items: flex-start;\n margin-top: 8px;\n padding: 12px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--ui-crit, #c62828) 8%, transparent);\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n font-size: var(--ui-t-body, 13px);\n --md-icon-size: 18px;\n }\n\n .issue md-icon {\n flex: none;\n color: var(--ui-crit, #c62828);\n }\n\n .issue.access {\n background: var(--ui-panel-2, var(--md-sys-color-surface-container));\n }\n\n .issue.access md-icon {\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n }\n\n .nothing {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: 100%;\n min-height: 120px;\n color: var(--ui-faint, var(--md-sys-color-on-surface-variant));\n font-size: var(--ui-t-body, 13px);\n text-align: center;\n --md-icon-size: 28px;\n }\n\n footer {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px 10px;\n padding: 6px 16px 12px;\n font-size: var(--ui-t-micro, 11px);\n color: var(--ui-faint, var(--md-sys-color-on-surface-variant));\n }\n\n footer .partial {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--ui-warn, #b26a00);\n --md-icon-size: 14px;\n }\n\n .edit {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 8px;\n border-top: 1px dashed var(--ui-line, var(--md-sys-color-outline-variant));\n background: var(--ui-panel-2, var(--md-sys-color-surface-container-low));\n }\n\n .edit .size {\n margin: 0 6px;\n font-size: var(--ui-t-small, 12px);\n color: var(--ui-muted, var(--md-sys-color-on-surface-variant));\n font-variant-numeric: tabular-nums;\n }\n `\n\n @property({ attribute: false }) card?: CardItem\n /** The place's size, when it is on the dashboard. */\n @property({ type: Number }) width = 2\n @property({ type: Number }) height = 1\n @property({ type: Boolean, reflect: true }) editing = false\n @property({ type: Boolean }) first = false\n @property({ type: Boolean }) last = false\n\n @state() private run?: CardRun\n @state() private failed?: string\n @state() private loading = false\n @state() private menu = false\n @state() private readAt?: Date\n\n private readingFor?: string\n private closeMenu = (event: Event) => {\n if (!event.composedPath().includes(this)) this.menu = false\n }\n\n connectedCallback() {\n super.connectedCallback()\n document.addEventListener('click', this.closeMenu, true)\n }\n\n disconnectedCallback() {\n document.removeEventListener('click', this.closeMenu, true)\n super.disconnectedCallback()\n }\n\n updated(changes: Map<string, unknown>) {\n /* A new card, or a new version of it, is read again; nothing else is. */\n const key = this.card ? `${this.card.id}@${this.card.version}` : undefined\n if (changes.has('card') && key && key !== this.readingFor) this.refresh()\n }\n\n /** Draws the card again from the data - the server runs its definition as the viewer. */\n async refresh() {\n if (!this.card) return\n const key = `${this.card.id}@${this.card.version}`\n this.readingFor = key\n this.loading = true\n this.failed = undefined\n try {\n const run = await cardApi.run(this.card.id)\n if (this.readingFor !== key) return\n this.run = run\n this.readAt = new Date()\n } catch (error: any) {\n if (this.readingFor === key) this.failed = error?.message ?? String(error)\n } finally {\n if (this.readingFor === key) this.loading = false\n }\n }\n\n private act(action: CardAction) {\n this.menu = false\n if (action === 'refresh') return this.refresh()\n this.dispatchEvent(new CustomEvent('card-action', { detail: { action, card: this.card }, bubbles: true, composed: true }))\n }\n\n render() {\n const card = this.card\n if (!card) return nothing\n const owner = card.access === 'owner'\n const source = this.run?.source?.labelKey ? t(this.run.source.labelKey) : ''\n const ownership = ownershipLine(card, t)\n const sub = [card.description || source, ownership].filter(Boolean).join(' · ')\n return html`<article aria-busy=${this.loading ? 'true' : 'false'}>\n <header>\n <span class=\"kind\"><md-icon>${viewIcon(card.definition)}</md-icon></span>\n <div class=\"titles\">\n <h4 title=${card.name}>${card.name}</h4>\n ${sub ? html`<span class=\"sub\" title=${sub}>${sub}</span>` : nothing}\n </div>\n <div class=\"tools\">\n <button class=\"icon ${this.loading ? 'spin' : ''}\" title=${t('ai-dashboard.card.refresh')} aria-label=${t('ai-dashboard.card.refresh')} ?disabled=${this.loading} @click=${() => this.act('refresh')}>\n <md-icon>refresh</md-icon>\n </button>\n ${owner\n ? html`<button class=\"icon\" title=${t('ai-dashboard.card.ai')} aria-label=${t('ai-dashboard.card.ai')} @click=${() => this.act('ai')}><md-icon>auto_awesome</md-icon></button>`\n : nothing}\n <button class=\"icon\" title=${t('ai-dashboard.card.more')} aria-label=${t('ai-dashboard.card.more')} aria-expanded=${this.menu ? 'true' : 'false'} @click=${() => (this.menu = !this.menu)}>\n <md-icon>more_vert</md-icon>\n </button>\n </div>\n </header>\n ${this.menu ? this.renderMenu(owner) : nothing}\n <div class=\"body\">${this.renderBody()}</div>\n ${this.run && !this.run.issue ? this.renderFooter() : nothing}\n ${this.editing ? this.renderEdit() : nothing}\n </article>`\n }\n\n private renderMenu(owner: boolean) {\n const item = (action: CardAction, icon: string, key: string) => html`<button role=\"menuitem\" @click=${() => this.act(action)}><md-icon>${icon}</md-icon>${t(key)}</button>`\n return html`<div class=\"menu\" role=\"menu\">\n ${owner ? item('ai', 'auto_awesome', 'ai-dashboard.card.ai') : nothing} ${owner ? item('rename', 'edit', 'ai-dashboard.card.rename') : nothing}\n ${owner ? item('share', 'share', 'ai-dashboard.card.share') : nothing} ${owner ? item('versions', 'history', 'ai-dashboard.card.versions') : nothing}\n ${item('copy', 'content_copy', owner ? 'ai-dashboard.card.duplicate' : 'ai-dashboard.card.copy')}\n <hr />\n ${item('unplace', 'remove_circle_outline', 'ai-dashboard.card.unplace')}\n </div>`\n }\n\n private renderBody() {\n if (this.failed) return html`<div class=\"issue\"><md-icon>error</md-icon><span>${this.failed}</span></div>`\n if (!this.run) return html`<div class=\"skeleton\" aria-hidden=\"true\"><span></span><span></span><span></span></div>`\n const issue = issueLine(this.run, t)\n if (issue) return html`<div class=\"issue ${issue.access ? 'access' : ''}\"><md-icon>${issue.access ? 'lock' : 'error'}</md-icon><span>${issue.text}</span></div>`\n if (drawsNothing(this.run)) return html`<div class=\"nothing\"><md-icon>inbox</md-icon><span>${t('ai-dashboard.card.nothing')}</span></div>`\n return html`<ox-assistant-workspace-view compact .workspace=${{ name: this.card!.id, title: this.card!.name, blocks: [this.run.block] }}></ox-assistant-workspace-view>`\n }\n\n private renderFooter() {\n const partial = partialLine(this.run!, t)\n return html`<footer>\n <span>${readLine(this.run!, this.readAt ?? new Date(), t, i18next.language)}</span>\n ${partial ? html`<span class=\"partial\"><md-icon>warning</md-icon>${partial}</span>` : nothing}\n </footer>`\n }\n\n private renderEdit() {\n const button = (action: CardAction, icon: string, key: string, disabled = false) =>\n html`<button class=\"icon\" title=${t(key)} aria-label=${t(key)} ?disabled=${disabled} @click=${() => this.act(action)}><md-icon>${icon}</md-icon></button>`\n return html`<div class=\"edit\">\n ${button('left', 'arrow_back', 'ai-dashboard.edit.left', this.first)} ${button('right', 'arrow_forward', 'ai-dashboard.edit.right', this.last)}\n ${button('narrower', 'remove', 'ai-dashboard.edit.narrower', this.width <= 1)}\n <span class=\"size\">${this.width} × ${this.height}</span>\n ${button('wider', 'add', 'ai-dashboard.edit.wider', this.width >= 4)}\n ${button(this.height > 1 ? 'shorter' : 'taller', this.height > 1 ? 'unfold_less' : 'unfold_more', this.height > 1 ? 'ai-dashboard.edit.shorter' : 'ai-dashboard.edit.taller')}\n ${button('unplace', 'delete_outline', 'ai-dashboard.card.unplace')}\n </div>`\n }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import '@material/web/icon/icon.js';
2
+ import '@things-factory/ai-assistant/dist-client/index.js';
3
+ import { LitElement } from 'lit';
4
+ declare const ProposalPreview_base: (new (...args: any[]) => LitElement) & typeof LitElement;
5
+ export declare class ProposalPreview extends ProposalPreview_base {
6
+ static styles: import("lit").CSSResult;
7
+ proposal: any;
8
+ sessionId?: string;
9
+ proposalKey?: string;
10
+ private run?;
11
+ private failed?;
12
+ private readFor?;
13
+ updated(): void;
14
+ private read;
15
+ render(): import("lit-html").TemplateResult<1>;
16
+ }
17
+ export {};
@@ -0,0 +1,111 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ /*
3
+ * The preview under a card the conversation offers (ADR-0160 decisions 6, 9). The proposal holds the definition only;
4
+ * the preview asks the server to run it as whoever is looking at the conversation, so a value is never kept in the
5
+ * conversation and nobody sees data their own doors do not open.
6
+ */
7
+ import '@material/web/icon/icon.js';
8
+ import '@things-factory/ai-assistant/dist-client/index.js';
9
+ import { LitElement, css, html, nothing } from 'lit';
10
+ import { customElement, property, state } from 'lit/decorators.js';
11
+ import { i18next, localize } from '@operato/i18n';
12
+ import { cardApi } from '../utils/card-api.js';
13
+ import { drawsNothing, issueLine, partialLine } from '../utils/card-words.js';
14
+ const t = (key, options = {}) => String(i18next.t(key, options));
15
+ let ProposalPreview = class ProposalPreview extends localize(i18next)(LitElement) {
16
+ static { this.styles = css `
17
+ :host {
18
+ display: block;
19
+ margin: 4px 0 8px;
20
+ }
21
+
22
+ .frame {
23
+ padding: 10px 12px 8px;
24
+ border: 1px solid var(--md-sys-color-outline-variant);
25
+ border-radius: 10px;
26
+ background: var(--md-sys-color-surface-container-lowest);
27
+ }
28
+
29
+ .waiting,
30
+ .note {
31
+ font-size: 12px;
32
+ color: var(--md-sys-color-on-surface-variant);
33
+ }
34
+
35
+ .note {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 4px;
39
+ margin-top: 6px;
40
+ --md-icon-size: 14px;
41
+ }
42
+
43
+ .issue {
44
+ font-size: 12px;
45
+ color: var(--md-sys-color-error);
46
+ }
47
+
48
+ .partial {
49
+ color: var(--ui-warn, #b26a00);
50
+ }
51
+ `; }
52
+ updated() {
53
+ const key = this.sessionId && this.proposalKey ? `${this.sessionId}/${this.proposalKey}` : undefined;
54
+ if (key && key !== this.readFor)
55
+ this.read(key);
56
+ }
57
+ async read(key) {
58
+ this.readFor = key;
59
+ try {
60
+ const run = await cardApi.preview(this.sessionId, this.proposalKey);
61
+ if (this.readFor === key)
62
+ this.run = run;
63
+ }
64
+ catch (error) {
65
+ if (this.readFor === key)
66
+ this.failed = error?.message ?? String(error);
67
+ }
68
+ }
69
+ render() {
70
+ if (this.failed)
71
+ return html `<div class="frame issue">${this.failed}</div>`;
72
+ if (!this.run)
73
+ return html `<div class="frame waiting">${t('ai-dashboard.preview.reading')}</div>`;
74
+ const issue = issueLine(this.run, t);
75
+ const partial = partialLine(this.run, t);
76
+ return html `<div class="frame">
77
+ ${issue
78
+ ? html `<div class="issue">${issue.text}</div>`
79
+ : drawsNothing(this.run)
80
+ ? html `<div class="waiting">${t('ai-dashboard.card.nothing')}</div>`
81
+ : html `<ox-assistant-workspace-view compact .workspace=${{ name: this.proposalKey, title: this.proposal?.card?.name ?? '', blocks: [this.run.block] }}></ox-assistant-workspace-view>`}
82
+ ${partial ? html `<div class="note partial"><md-icon>warning</md-icon>${partial}</div>` : nothing}
83
+ <div class="note"><md-icon>bolt</md-icon>${t('ai-dashboard.preview.note', { day: this.run.asOf })}</div>
84
+ </div>`;
85
+ }
86
+ };
87
+ __decorate([
88
+ property({ attribute: false }),
89
+ __metadata("design:type", Object)
90
+ ], ProposalPreview.prototype, "proposal", void 0);
91
+ __decorate([
92
+ property({ attribute: false }),
93
+ __metadata("design:type", String)
94
+ ], ProposalPreview.prototype, "sessionId", void 0);
95
+ __decorate([
96
+ property({ attribute: false }),
97
+ __metadata("design:type", String)
98
+ ], ProposalPreview.prototype, "proposalKey", void 0);
99
+ __decorate([
100
+ state(),
101
+ __metadata("design:type", Object)
102
+ ], ProposalPreview.prototype, "run", void 0);
103
+ __decorate([
104
+ state(),
105
+ __metadata("design:type", String)
106
+ ], ProposalPreview.prototype, "failed", void 0);
107
+ ProposalPreview = __decorate([
108
+ customElement('ai-dashboard-proposal-preview')
109
+ ], ProposalPreview);
110
+ export { ProposalPreview };
111
+ //# sourceMappingURL=proposal-preview.js.map