@things-factory/ai-assistant 10.1.98 → 10.1.100

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 (36) hide show
  1. package/client/components/entry-review.ts +27 -8
  2. package/client/components/workspace-view.ts +6 -1
  3. package/client/utils/entry-proposal.ts +2 -1
  4. package/dist-client/components/entry-review.d.ts +1 -1
  5. package/dist-client/components/entry-review.js +27 -7
  6. package/dist-client/components/entry-review.js.map +1 -1
  7. package/dist-client/components/workspace-view.d.ts +1 -1
  8. package/dist-client/components/workspace-view.js +7 -2
  9. package/dist-client/components/workspace-view.js.map +1 -1
  10. package/dist-client/tsconfig.tsbuildinfo +1 -1
  11. package/dist-client/utils/entry-proposal.d.ts +2 -0
  12. package/dist-client/utils/entry-proposal.js.map +1 -1
  13. package/dist-server/service/data-entry/entry-apply.js +2 -0
  14. package/dist-server/service/data-entry/entry-apply.js.map +1 -1
  15. package/dist-server/service/data-entry/entry-target.d.ts +54 -4
  16. package/dist-server/service/data-entry/entry-target.js +115 -10
  17. package/dist-server/service/data-entry/entry-target.js.map +1 -1
  18. package/dist-server/service/data-entry/entry-tools.d.ts +2 -1
  19. package/dist-server/service/data-entry/entry-tools.js +27 -10
  20. package/dist-server/service/data-entry/entry-tools.js.map +1 -1
  21. package/dist-server/service/data-entry/entry-update.d.ts +1 -0
  22. package/dist-server/service/data-entry/entry-update.js +4 -4
  23. package/dist-server/service/data-entry/entry-update.js.map +1 -1
  24. package/dist-server/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +2 -2
  26. package/server/service/data-entry/data-entry-update.test.ts +61 -1
  27. package/server/service/data-entry/data-entry.test.ts +36 -4
  28. package/server/service/data-entry/entry-apply.ts +3 -1
  29. package/server/service/data-entry/entry-target.ts +139 -14
  30. package/server/service/data-entry/entry-tools.ts +28 -10
  31. package/server/service/data-entry/entry-update.ts +5 -4
  32. package/translations/en.json +7 -1
  33. package/translations/ja.json +7 -1
  34. package/translations/ko.json +7 -1
  35. package/translations/ms.json +7 -1
  36. package/translations/zh.json +7 -1
@@ -11,6 +11,7 @@ import { LitElement, css, html, nothing } from 'lit'
11
11
  import { customElement, property } from 'lit/decorators.js'
12
12
 
13
13
  import { i18next } from '@operato/i18n'
14
+ import { ScrollbarStyles } from '@operato/styles'
14
15
 
15
16
  import { entryCellIssues, entryIssueKey, type EntryIssueView, type EntryRowView, type EntryTotalView } from '../utils/entry-proposal.js'
16
17
 
@@ -25,8 +26,22 @@ function issueText(issue: EntryIssueView): string {
25
26
  return String(i18next.t(entryIssueKey(issue), { defaultValue: issue.code, ...params }))
26
27
  }
27
28
 
29
+ /** Where a row came from, in the screen's words - "said in the conversation" is the framework's, never the model's English. */
30
+ function sourceText(source: { attachmentId?: string; where?: string; person?: boolean }): string {
31
+ if (source.person) return t('source-person', 'said in the conversation')
32
+ return source.where || source.attachmentId || ''
33
+ }
34
+
35
+ /** An enum value in the person's words when the target names its key - the code only when it does not. */
36
+ function valueOf(column: { valueKeyPrefix?: string }, value: unknown): string {
37
+ if (column.valueKeyPrefix && typeof value === 'string' && value) return String(i18next.t(`${column.valueKeyPrefix}${value.toLowerCase()}`, { defaultValue: value }))
38
+ return shown(value)
39
+ }
40
+
28
41
  function shown(value: unknown): string {
29
42
  if (value === undefined || value === null) return ''
43
+ /* A reference: the record's name the server found, or the text that found none. */
44
+ if (typeof value === 'object') return String((value as any).label ?? (value as any).text ?? (value as any).id ?? '')
30
45
  if (typeof value === 'number') return value.toLocaleString(i18next.language || undefined)
31
46
  if (typeof value === 'boolean') return value ? '✓' : '—'
32
47
  return String(value)
@@ -34,7 +49,10 @@ function shown(value: unknown): string {
34
49
 
35
50
  @customElement('ox-assistant-entry-review')
36
51
  export class AssistantEntryReview extends LitElement {
37
- static styles = css`
52
+ /* The inner scroll areas wear the standard scrollbar, as the dock does. */
53
+ static styles = [
54
+ ScrollbarStyles,
55
+ css`
38
56
  :host {
39
57
  display: block;
40
58
  margin: 4px 0 2px;
@@ -163,6 +181,7 @@ export class AssistantEntryReview extends LitElement {
163
181
  color: var(--md-sys-color-on-surface-variant);
164
182
  }
165
183
  `
184
+ ]
166
185
 
167
186
  @property({ attribute: false }) proposal: any
168
187
 
@@ -178,7 +197,7 @@ export class AssistantEntryReview extends LitElement {
178
197
  <span>${t('total', '{field} total {sum}', { field: name(total.field), sum: shown(total.all) })}</span>
179
198
  ${total.ready !== total.all ? html`<span>${t('total-ready', 'entered {sum}', { sum: shown(total.ready) })}</span>` : nothing}
180
199
  ${total.declared !== undefined
181
- ? html`<span>${t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>
200
+ ? html`<span>${total.declaredBy === 'file' ? t('total-declared-file', "the file's total line {sum}", { sum: shown(total.declared) }) : t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>
182
201
  ${total.difference
183
202
  ? html`<span class="differs">${t('total-differs', 'differs by {sum}', { sum: shown(total.difference) })}</span>`
184
203
  : html`<span class="matches"><md-icon>task_alt</md-icon> ${t('total-matches', 'matches')}</span>`}`
@@ -192,7 +211,7 @@ export class AssistantEntryReview extends LitElement {
192
211
  * record was picked. The "now" is the server's reading, never the model's memory.
193
212
  */
194
213
  private renderChanges(p: any) {
195
- const columns: { name: string; type: string; labelKey?: string }[] = p.columns ?? []
214
+ const columns: { name: string; type: string; labelKey?: string; valueKeyPrefix?: string }[] = p.columns ?? []
196
215
  const rows: any[] = p.rows ?? []
197
216
  const counts = p.counts ?? {}
198
217
  const via = (row: any) =>
@@ -225,7 +244,7 @@ export class AssistantEntryReview extends LitElement {
225
244
  const bad = issues.some(issue => issue.blocking)
226
245
  const changes = column.name in (row.values ?? {})
227
246
  return html`<td class="${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}" title=${issues.map(issueText).join('\n')}>
228
- ${changes ? html`<span class="before">${shown(row.before?.[column.name]) || '—'}</span> → <strong>${shown(row.values[column.name]) || '—'}</strong>` : ''}
247
+ ${changes ? html`<span class="before">${valueOf(column, row.before?.[column.name]) || '—'}</span> → <strong>${valueOf(column, row.values[column.name]) || '—'}</strong>` : ''}
229
248
  </td>`
230
249
  })}
231
250
  <td class="source">${via(row)}</td>
@@ -240,7 +259,7 @@ export class AssistantEntryReview extends LitElement {
240
259
  render() {
241
260
  const p = this.proposal ?? {}
242
261
  if (p.op === 'update') return this.renderChanges(p)
243
- const columns: { name: string; type: string; required?: boolean; labelKey?: string }[] = p.columns ?? []
262
+ const columns: { name: string; type: string; required?: boolean; labelKey?: string; valueKeyPrefix?: string }[] = p.columns ?? []
244
263
  const rows: EntryRowView[] = p.rows ?? []
245
264
  const counts = p.counts ?? {}
246
265
  return html`
@@ -272,7 +291,7 @@ export class AssistantEntryReview extends LitElement {
272
291
  const issues = entryCellIssues(row, column.name)
273
292
  const bad = issues.some(issue => issue.blocking)
274
293
  const value = row.values?.[column.name]
275
- const text = shown(value)
294
+ const text = valueOf(column, value)
276
295
  return html`<td
277
296
  class="${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}"
278
297
  title=${issues.map(issueText).join('\n')}
@@ -280,9 +299,9 @@ export class AssistantEntryReview extends LitElement {
280
299
  ${text || (issues.some(issue => issue.code === 'required') ? html`<span class="missing">${t('missing', 'missing')}</span>` : '')}
281
300
  </td>`
282
301
  })}
283
- <td class="source" title=${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}>
302
+ <td class="source" title=${(row.sources ?? []).map(sourceText).join(' · ')}>
284
303
  ${row.evidence ? html`<span class="verified" title=${t('evidence', 'found on line {line} of the file', { line: row.evidence.line })}><md-icon>task_alt</md-icon></span> ` : nothing}
285
- ${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}
304
+ ${(row.sources ?? []).map(sourceText).join(' · ')}
286
305
  ${whole
287
306
  .filter(issue => issue.code !== 'no-source')
288
307
  .map(issue => html`<span class="row-issue ${issue.blocking ? 'blocking' : ''}">${issueText(issue)}</span>`)}
@@ -12,6 +12,7 @@ import { customElement, property, state } from 'lit/decorators.js'
12
12
  import { unsafeHTML } from 'lit/directives/unsafe-html.js'
13
13
 
14
14
  import { i18next } from '@operato/i18n'
15
+ import { ScrollbarStyles } from '@operato/styles'
15
16
 
16
17
  import { renderMarkdown } from './markdown.js'
17
18
  import { sortRows, tableCsv, type WorkspaceBlockView, type WorkspaceView } from '../utils/workspace.js'
@@ -36,7 +37,10 @@ function ticksFor(max: number): number[] {
36
37
 
37
38
  @customElement('ox-assistant-workspace-view')
38
39
  export class AssistantWorkspaceView extends LitElement {
39
- static styles = css`
40
+ /* The inner scroll areas wear the standard scrollbar, as the dock does. */
41
+ static styles = [
42
+ ScrollbarStyles,
43
+ css`
40
44
  :host {
41
45
  display: block;
42
46
  color: var(--md-sys-color-on-surface);
@@ -243,6 +247,7 @@ export class AssistantWorkspaceView extends LitElement {
243
247
  margin-top: 0;
244
248
  }
245
249
  `
250
+ ]
246
251
 
247
252
  @property({ attribute: false }) workspace?: WorkspaceView
248
253
 
@@ -18,7 +18,7 @@ export interface EntryIssueView {
18
18
  export interface EntryRowView {
19
19
  index: number
20
20
  values: Record<string, unknown>
21
- sources: { attachmentId?: string; where?: string }[]
21
+ sources: { attachmentId?: string; where?: string; person?: boolean }[]
22
22
  issues: EntryIssueView[]
23
23
  /** The server found this row's numbers and dates together on this line of the cited file. */
24
24
  evidence?: { attachmentId: string; line: number }
@@ -31,6 +31,7 @@ export interface EntryTotalView {
31
31
  declared?: number
32
32
  declaredFound?: boolean
33
33
  difference?: number
34
+ declaredBy?: 'model' | 'file'
34
35
  }
35
36
 
36
37
  export interface EntryRowResult {
@@ -1,7 +1,7 @@
1
1
  import '@material/web/icon/icon.js';
2
2
  import { LitElement } from 'lit';
3
3
  export declare class AssistantEntryReview extends LitElement {
4
- static styles: import("lit").CSSResult;
4
+ static styles: import("lit").CSSResult[];
5
5
  proposal: any;
6
6
  /** The sum of each totalled field, beside the total the material prints - a missing row shows here without reading every row. */
7
7
  private renderTotals;
@@ -10,6 +10,7 @@ import '@material/web/icon/icon.js';
10
10
  import { LitElement, css, html, nothing } from 'lit';
11
11
  import { customElement, property } from 'lit/decorators.js';
12
12
  import { i18next } from '@operato/i18n';
13
+ import { ScrollbarStyles } from '@operato/styles';
13
14
  import { entryCellIssues, entryIssueKey } from '../utils/entry-proposal.js';
14
15
  const SHOWN = 50;
15
16
  const t = (key, defaultValue, options = {}) => i18next.t(`ai-assistant.entry.${key}`, { defaultValue, ...options });
@@ -19,9 +20,24 @@ function issueText(issue) {
19
20
  params.values = params.values.join(', ');
20
21
  return String(i18next.t(entryIssueKey(issue), { defaultValue: issue.code, ...params }));
21
22
  }
23
+ /** Where a row came from, in the screen's words - "said in the conversation" is the framework's, never the model's English. */
24
+ function sourceText(source) {
25
+ if (source.person)
26
+ return t('source-person', 'said in the conversation');
27
+ return source.where || source.attachmentId || '';
28
+ }
29
+ /** An enum value in the person's words when the target names its key - the code only when it does not. */
30
+ function valueOf(column, value) {
31
+ if (column.valueKeyPrefix && typeof value === 'string' && value)
32
+ return String(i18next.t(`${column.valueKeyPrefix}${value.toLowerCase()}`, { defaultValue: value }));
33
+ return shown(value);
34
+ }
22
35
  function shown(value) {
23
36
  if (value === undefined || value === null)
24
37
  return '';
38
+ /* A reference: the record's name the server found, or the text that found none. */
39
+ if (typeof value === 'object')
40
+ return String(value.label ?? value.text ?? value.id ?? '');
25
41
  if (typeof value === 'number')
26
42
  return value.toLocaleString(i18next.language || undefined);
27
43
  if (typeof value === 'boolean')
@@ -29,7 +45,10 @@ function shown(value) {
29
45
  return String(value);
30
46
  }
31
47
  let AssistantEntryReview = class AssistantEntryReview extends LitElement {
32
- static { this.styles = css `
48
+ /* The inner scroll areas wear the standard scrollbar, as the dock does. */
49
+ static { this.styles = [
50
+ ScrollbarStyles,
51
+ css `
33
52
  :host {
34
53
  display: block;
35
54
  margin: 4px 0 2px;
@@ -157,7 +176,8 @@ let AssistantEntryReview = class AssistantEntryReview extends LitElement {
157
176
  margin-top: 4px;
158
177
  color: var(--md-sys-color-on-surface-variant);
159
178
  }
160
- `; }
179
+ `
180
+ ]; }
161
181
  /** The sum of each totalled field, beside the total the material prints - a missing row shows here without reading every row. */
162
182
  renderTotals(totals, columns) {
163
183
  if (!totals.length)
@@ -171,7 +191,7 @@ let AssistantEntryReview = class AssistantEntryReview extends LitElement {
171
191
  <span>${t('total', '{field} total {sum}', { field: name(total.field), sum: shown(total.all) })}</span>
172
192
  ${total.ready !== total.all ? html `<span>${t('total-ready', 'entered {sum}', { sum: shown(total.ready) })}</span>` : nothing}
173
193
  ${total.declared !== undefined
174
- ? html `<span>${t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>
194
+ ? html `<span>${total.declaredBy === 'file' ? t('total-declared-file', "the file's total line {sum}", { sum: shown(total.declared) }) : t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>
175
195
  ${total.difference
176
196
  ? html `<span class="differs">${t('total-differs', 'differs by {sum}', { sum: shown(total.difference) })}</span>`
177
197
  : html `<span class="matches"><md-icon>task_alt</md-icon> ${t('total-matches', 'matches')}</span>`}`
@@ -216,7 +236,7 @@ let AssistantEntryReview = class AssistantEntryReview extends LitElement {
216
236
  const bad = issues.some(issue => issue.blocking);
217
237
  const changes = column.name in (row.values ?? {});
218
238
  return html `<td class="${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}" title=${issues.map(issueText).join('\n')}>
219
- ${changes ? html `<span class="before">${shown(row.before?.[column.name]) || '—'}</span> → <strong>${shown(row.values[column.name]) || '—'}</strong>` : ''}
239
+ ${changes ? html `<span class="before">${valueOf(column, row.before?.[column.name]) || '—'}</span> → <strong>${valueOf(column, row.values[column.name]) || '—'}</strong>` : ''}
220
240
  </td>`;
221
241
  })}
222
242
  <td class="source">${via(row)}</td>
@@ -263,7 +283,7 @@ let AssistantEntryReview = class AssistantEntryReview extends LitElement {
263
283
  const issues = entryCellIssues(row, column.name);
264
284
  const bad = issues.some(issue => issue.blocking);
265
285
  const value = row.values?.[column.name];
266
- const text = shown(value);
286
+ const text = valueOf(column, value);
267
287
  return html `<td
268
288
  class="${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}"
269
289
  title=${issues.map(issueText).join('\n')}
@@ -271,9 +291,9 @@ let AssistantEntryReview = class AssistantEntryReview extends LitElement {
271
291
  ${text || (issues.some(issue => issue.code === 'required') ? html `<span class="missing">${t('missing', 'missing')}</span>` : '')}
272
292
  </td>`;
273
293
  })}
274
- <td class="source" title=${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}>
294
+ <td class="source" title=${(row.sources ?? []).map(sourceText).join(' · ')}>
275
295
  ${row.evidence ? html `<span class="verified" title=${t('evidence', 'found on line {line} of the file', { line: row.evidence.line })}><md-icon>task_alt</md-icon></span> ` : nothing}
276
- ${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}
296
+ ${(row.sources ?? []).map(sourceText).join(' · ')}
277
297
  ${whole
278
298
  .filter(issue => issue.code !== 'no-source')
279
299
  .map(issue => html `<span class="row-issue ${issue.blocking ? 'blocking' : ''}">${issueText(issue)}</span>`)}
@@ -1 +1 @@
1
- {"version":3,"file":"entry-review.js","sourceRoot":"","sources":["../../client/components/entry-review.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The rows of a data entry proposal, as the person reviews them (operato-application/design/ai/data-entry.md §5).\n *\n * Read-only on purpose: a correction is said in the conversation (\"row 2 is 12,000\", \"drop the last one\")\n * and the model proposes again. What is wrong in a cell is marked on the cell - blocking in the error\n * colour, a note in the outline - and where each row came from is on the row.\n */\nimport '@material/web/icon/icon.js'\n\nimport { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { entryCellIssues, entryIssueKey, type EntryIssueView, type EntryRowView, type EntryTotalView } from '../utils/entry-proposal.js'\n\nconst SHOWN = 50\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n i18next.t(`ai-assistant.entry.${key}`, { defaultValue, ...options })\n\nfunction issueText(issue: EntryIssueView): string {\n const params = { ...(issue.params ?? {}) } as any\n if (Array.isArray(params.values)) params.values = params.values.join(', ')\n return String(i18next.t(entryIssueKey(issue), { defaultValue: issue.code, ...params }))\n}\n\nfunction shown(value: unknown): string {\n if (value === undefined || value === null) return ''\n if (typeof value === 'number') return value.toLocaleString(i18next.language || undefined)\n if (typeof value === 'boolean') return value ? '✓' : '—'\n return String(value)\n}\n\n@customElement('ox-assistant-entry-review')\nexport class AssistantEntryReview extends LitElement {\n static styles = css`\n :host {\n display: block;\n margin: 4px 0 2px;\n font-size: 12px;\n color: var(--md-sys-color-on-surface);\n }\n\n .summary {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 10px;\n margin-bottom: 6px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .summary .blocked {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n\n .scroll {\n max-height: 320px;\n overflow: auto;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n\n table {\n width: 100%;\n border-collapse: collapse;\n }\n\n th,\n td {\n padding: 4px 6px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n text-align: start;\n vertical-align: top;\n white-space: nowrap;\n }\n\n th {\n position: sticky;\n top: 0;\n background: var(--md-sys-color-surface-container);\n font-weight: 600;\n }\n\n td.num {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n tr.blocked td {\n background: color-mix(in srgb, var(--md-sys-color-error) 6%, transparent);\n }\n\n td.bad {\n color: var(--md-sys-color-error);\n text-decoration: underline wavy var(--md-sys-color-error);\n text-underline-offset: 3px;\n }\n\n td.note {\n text-decoration: underline dotted var(--md-sys-color-outline);\n text-underline-offset: 3px;\n }\n\n .before {\n color: var(--md-sys-color-on-surface-variant);\n text-decoration: line-through;\n }\n\n .missing {\n color: var(--md-sys-color-error);\n font-style: italic;\n }\n\n .source {\n color: var(--md-sys-color-on-surface-variant);\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .row-issue {\n display: block;\n color: var(--md-sys-color-on-surface-variant);\n white-space: normal;\n }\n\n .row-issue.blocking {\n color: var(--md-sys-color-error);\n }\n\n .verified {\n color: var(--md-sys-color-primary);\n }\n\n .verified md-icon,\n .totals md-icon {\n --md-icon-size: 14px;\n vertical-align: -2px;\n }\n\n .totals {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n margin: 6px 0 0;\n font-variant-numeric: tabular-nums;\n }\n\n .totals .differs {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n\n .totals .matches {\n color: var(--md-sys-color-primary);\n }\n\n .more,\n .check-failed {\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n `\n\n @property({ attribute: false }) proposal: any\n\n /** The sum of each totalled field, beside the total the material prints - a missing row shows here without reading every row. */\n private renderTotals(totals: EntryTotalView[], columns: { name: string; labelKey?: string }[]) {\n if (!totals.length) return nothing\n const name = (field: string) => {\n const column = columns.find(one => one.name === field)\n return column?.labelKey ? i18next.t(column.labelKey) : field\n }\n return html`<div class=\"totals\" role=\"status\">\n ${totals.map(total => html`\n <span>${t('total', '{field} total {sum}', { field: name(total.field), sum: shown(total.all) })}</span>\n ${total.ready !== total.all ? html`<span>${t('total-ready', 'entered {sum}', { sum: shown(total.ready) })}</span>` : nothing}\n ${total.declared !== undefined\n ? html`<span>${t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>\n ${total.difference\n ? html`<span class=\"differs\">${t('total-differs', 'differs by {sum}', { sum: shown(total.difference) })}</span>`\n : html`<span class=\"matches\"><md-icon>task_alt</md-icon> ${t('total-matches', 'matches')}</span>`}`\n : nothing}\n `)}\n </div>`\n }\n\n /**\n * A 수정안 - each record by its name, each changing field as what it holds now and what it becomes, and how the\n * record was picked. The \"now\" is the server's reading, never the model's memory.\n */\n private renderChanges(p: any) {\n const columns: { name: string; type: string; labelKey?: string }[] = p.columns ?? []\n const rows: any[] = p.rows ?? []\n const counts = p.counts ?? {}\n const via = (row: any) =>\n row.via === 'current' ? t('via-current', 'the record on screen') : row.via === 'selected' ? t('via-selected', 'selected on screen') : t('via-id', 'found in the conversation')\n return html`\n <div class=\"summary\" role=\"status\">\n <span>${t('changes', '{count} records', { count: counts.rows ?? rows.length })}</span>\n <span>${t('ready', '{count} ready', { count: counts.ready ?? 0 })}</span>\n ${counts.blocked ? html`<span class=\"blocked\">${t('not-changed', '{count} will not be changed', { count: counts.blocked })}</span>` : nothing}\n </div>\n <div class=\"scroll\">\n <table>\n <thead>\n <tr>\n <th>#</th>\n <th>${t('record', 'record')}</th>\n ${columns.map(column => html`<th>${column.labelKey ? i18next.t(column.labelKey) : column.name}</th>`)}\n <th>${t('picked', 'picked')}</th>\n </tr>\n </thead>\n <tbody>\n ${rows.slice(0, SHOWN).map(row => {\n const blocked = (row.issues ?? []).some((issue: EntryIssueView) => issue.blocking)\n const whole = entryCellIssues(row)\n return html`<tr class=${blocked ? 'blocked' : ''}>\n <td class=\"num\">${row.index + 1}</td>\n <td>${row.label || row.id}${whole.map(issue => html`<span class=\"row-issue ${issue.blocking ? 'blocking' : ''}\">${issueText(issue)}</span>`)}</td>\n ${columns.map(column => {\n const issues = entryCellIssues(row, column.name)\n const bad = issues.some(issue => issue.blocking)\n const changes = column.name in (row.values ?? {})\n return html`<td class=\"${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}\" title=${issues.map(issueText).join('\\n')}>\n ${changes ? html`<span class=\"before\">${shown(row.before?.[column.name]) || '—'}</span> → <strong>${shown(row.values[column.name]) || '—'}</strong>` : ''}\n </td>`\n })}\n <td class=\"source\">${via(row)}</td>\n </tr>`\n })}\n </tbody>\n </table>\n </div>\n `\n }\n\n render() {\n const p = this.proposal ?? {}\n if (p.op === 'update') return this.renderChanges(p)\n const columns: { name: string; type: string; required?: boolean; labelKey?: string }[] = p.columns ?? []\n const rows: EntryRowView[] = p.rows ?? []\n const counts = p.counts ?? {}\n return html`\n <div class=\"summary\" role=\"status\">\n <span>${t('rows', '{count} rows', { count: counts.rows ?? rows.length })}</span>\n <span>${t('ready', '{count} ready', { count: counts.ready ?? 0 })}</span>\n ${counts.blocked ? html`<span class=\"blocked\">${t('blocked', '{count} will not be entered', { count: counts.blocked })}</span>` : nothing}\n ${counts.cut ? html`<span class=\"blocked\">${t('cut', '{count} left out over the limit', { count: counts.cut })}</span>` : nothing}\n ${counts.verified ? html`<span class=\"verified\"><md-icon>task_alt</md-icon> ${t('verified', '{count} found in their file', { count: counts.verified })}</span>` : nothing}\n ${counts.unmatched ? html`<span class=\"blocked\">${t('unmatched', '{count} with a value their file does not have', { count: counts.unmatched })}</span>` : nothing}\n </div>\n <div class=\"scroll\">\n <table>\n <thead>\n <tr>\n <th>#</th>\n ${columns.map(column => html`<th>${column.labelKey ? i18next.t(column.labelKey) : column.name}${column.required ? ' *' : ''}</th>`)}\n <th>${t('source', 'from')}</th>\n </tr>\n </thead>\n <tbody>\n ${rows.slice(0, SHOWN).map(row => {\n const blocked = (row.issues ?? []).some(issue => issue.blocking)\n const whole = entryCellIssues(row)\n return html`\n <tr class=${blocked ? 'blocked' : ''}>\n <td class=\"num\">${row.index + 1}</td>\n ${columns.map(column => {\n const issues = entryCellIssues(row, column.name)\n const bad = issues.some(issue => issue.blocking)\n const value = row.values?.[column.name]\n const text = shown(value)\n return html`<td\n class=\"${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}\"\n title=${issues.map(issueText).join('\\n')}\n >\n ${text || (issues.some(issue => issue.code === 'required') ? html`<span class=\"missing\">${t('missing', 'missing')}</span>` : '')}\n </td>`\n })}\n <td class=\"source\" title=${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}>\n ${row.evidence ? html`<span class=\"verified\" title=${t('evidence', 'found on line {line} of the file', { line: row.evidence.line })}><md-icon>task_alt</md-icon></span> ` : nothing}\n ${(row.sources ?? []).map(source => source.where || source.attachmentId).join(' · ')}\n ${whole\n .filter(issue => issue.code !== 'no-source')\n .map(issue => html`<span class=\"row-issue ${issue.blocking ? 'blocking' : ''}\">${issueText(issue)}</span>`)}\n ${whole.some(issue => issue.code === 'no-source') ? html`<span class=\"row-issue\">${issueText({ code: 'no-source', blocking: false })}</span>` : nothing}\n </td>\n </tr>\n `\n })}\n </tbody>\n </table>\n </div>\n ${this.renderTotals(p.totals ?? [], columns)}\n ${rows.length > SHOWN ? html`<div class=\"more\">${t('more-rows', '{count} more rows are not shown - they are entered too.', { count: rows.length - SHOWN })}</div>` : nothing}\n ${p.checkFailed ? html`<div class=\"check-failed\">${t('check-failed', 'the application could not check these rows; they are checked again when entered.')}</div>` : nothing}\n `\n }\n}\n"]}
1
+ {"version":3,"file":"entry-review.js","sourceRoot":"","sources":["../../client/components/entry-review.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;AACH,OAAO,4BAA4B,CAAA;AAEnC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,aAAa,EAA+D,MAAM,4BAA4B,CAAA;AAExI,MAAM,KAAK,GAAG,EAAE,CAAA;AAEhB,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,YAAoB,EAAE,UAAmC,EAAE,EAAE,EAAE,CACrF,OAAO,CAAC,CAAC,CAAC,sBAAsB,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,CAAC,CAAA;AAEtE,SAAS,SAAS,CAAC,KAAqB;IACtC,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,EAAS,CAAA;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1E,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;AACzF,CAAC;AAED,+HAA+H;AAC/H,SAAS,UAAU,CAAC,MAAmE;IACrF,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,CAAC,eAAe,EAAE,0BAA0B,CAAC,CAAA;IACxE,OAAO,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,YAAY,IAAI,EAAE,CAAA;AAClD,CAAC;AAED,0GAA0G;AAC1G,SAAS,OAAO,CAAC,MAAmC,EAAE,KAAc;IAClE,IAAI,MAAM,CAAC,cAAc,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,cAAc,GAAG,KAAK,CAAC,WAAW,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IACpK,OAAO,KAAK,CAAC,KAAK,CAAC,CAAA;AACrB,CAAC;AAED,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAA;IACpD,mFAAmF;IACnF,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAE,KAAa,CAAC,KAAK,IAAK,KAAa,CAAC,IAAI,IAAK,KAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;IACpH,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAA;IACzF,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;IACxD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC;AAGM,IAAM,oBAAoB,GAA1B,MAAM,oBAAqB,SAAQ,UAAU;IAClD,2EAA2E;aACpE,WAAM,GAAG;QACd,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgIJ;KACA,AAnIY,CAmIZ;IAID,iIAAiI;IACzH,YAAY,CAAC,MAAwB,EAAE,OAA8C;QAC3F,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,OAAO,CAAA;QAClC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE;YAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,CAAA;YACtD,OAAO,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QAC9D,CAAC,CAAA;QACD,OAAO,IAAI,CAAA;QACP,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA;gBAChB,CAAC,CAAC,OAAO,EAAE,qBAAqB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;UAC5F,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA,SAAS,CAAC,CAAC,aAAa,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UAC1H,KAAK,CAAC,QAAQ,KAAK,SAAS;YAC5B,CAAC,CAAC,IAAI,CAAA,SAAS,KAAK,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,qBAAqB,EAAE,6BAA6B,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,yBAAyB,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,uBAAuB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBAC5S,KAAK,CAAC,UAAU;gBAChB,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,eAAe,EAAE,kBAAkB,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,SAAS;gBAChH,CAAC,CAAC,IAAI,CAAA,qDAAqD,CAAC,CAAC,eAAe,EAAE,SAAS,CAAC,SAAS,EAAE;YACzG,CAAC,CAAC,OAAO;OACZ,CAAC;WACG,CAAA;IACT,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,CAAM;QAC1B,MAAM,OAAO,GAAiF,CAAC,CAAC,OAAO,IAAI,EAAE,CAAA;QAC7G,MAAM,IAAI,GAAU,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;QAChC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,CAAA;QAC7B,MAAM,GAAG,GAAG,CAAC,GAAQ,EAAE,EAAE,CACvB,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,2BAA2B,CAAC,CAAA;QAChL,OAAO,IAAI,CAAA;;gBAEC,CAAC,CAAC,SAAS,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtE,CAAC,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;UAC/D,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,aAAa,EAAE,6BAA6B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;;;;;;oBAOjI,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC;oBAC/F,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC;;;;cAI3B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YAC/B,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAqB,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;YAClF,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;YAClC,OAAO,IAAI,CAAA,aAAa,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;kCAC5B,GAAG,CAAC,KAAK,GAAG,CAAC;sBACzB,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA,0BAA0B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC;kBAC1I,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBACrB,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;gBAChD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;gBAChD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;gBACjD,OAAO,IAAI,CAAA,cAAc,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;sBAC/K,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,wBAAwB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,qBAAqB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE;wBACzK,CAAA;YACR,CAAC,CAAC;qCACmB,GAAG,CAAC,GAAG,CAAC;oBACzB,CAAA;QACR,CAAC,CAAC;;;;KAIT,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAA;QAC7B,IAAI,CAAC,CAAC,EAAE,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;QACnD,MAAM,OAAO,GAAqG,CAAC,CAAC,OAAO,IAAI,EAAE,CAAA;QACjI,MAAM,IAAI,GAAmB,CAAC,CAAC,IAAI,IAAI,EAAE,CAAA;QACzC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,CAAA;QAC7B,OAAO,IAAI,CAAA;;gBAEC,CAAC,CAAC,MAAM,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChE,CAAC,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;UAC/D,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,SAAS,EAAE,6BAA6B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UACvI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,KAAK,EAAE,iCAAiC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UAC/H,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,sDAAsD,CAAC,CAAC,UAAU,EAAE,6BAA6B,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;UACvK,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,WAAW,EAAE,+CAA+C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;;;;;;gBAOzJ,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAA,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;oBAC7H,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC;;;;cAIzB,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YAC/B,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;YAChE,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;YAClC,OAAO,IAAI,CAAA;4BACG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;oCAChB,GAAG,CAAC,KAAK,GAAG,CAAC;oBAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;gBACrB,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;gBAChD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;gBAChD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACvC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;gBACnC,OAAO,IAAI,CAAA;+BACA,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;8BAChH,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;wBAEtC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;0BAC5H,CAAA;YACR,CAAC,CAAC;6CACyB,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;sBACtE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,gCAAgC,CAAC,CAAC,UAAU,EAAE,kCAAkC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,sCAAsC,CAAC,CAAC,CAAC,OAAO;sBACjL,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;sBAC/C,KAAK;iBACJ,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;iBAC3C,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAA,0BAA0B,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC;sBAC3G,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,2BAA2B,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;;eAG5J,CAAA;QACH,CAAC,CAAC;;;;QAIN,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,qBAAqB,CAAC,CAAC,WAAW,EAAE,yDAAyD,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;QAC1K,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA,6BAA6B,CAAC,CAAC,cAAc,EAAE,kFAAkF,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;KAC3K,CAAA;IACH,CAAC;;AAtI+B;IAA/B,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;;sDAAc;AAvIlC,oBAAoB;IADhC,aAAa,CAAC,2BAA2B,CAAC;GAC9B,oBAAoB,CA8QhC","sourcesContent":["/*\n * The rows of a data entry proposal, as the person reviews them (operato-application/design/ai/data-entry.md §5).\n *\n * Read-only on purpose: a correction is said in the conversation (\"row 2 is 12,000\", \"drop the last one\")\n * and the model proposes again. What is wrong in a cell is marked on the cell - blocking in the error\n * colour, a note in the outline - and where each row came from is on the row.\n */\nimport '@material/web/icon/icon.js'\n\nimport { LitElement, css, html, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { entryCellIssues, entryIssueKey, type EntryIssueView, type EntryRowView, type EntryTotalView } from '../utils/entry-proposal.js'\n\nconst SHOWN = 50\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n i18next.t(`ai-assistant.entry.${key}`, { defaultValue, ...options })\n\nfunction issueText(issue: EntryIssueView): string {\n const params = { ...(issue.params ?? {}) } as any\n if (Array.isArray(params.values)) params.values = params.values.join(', ')\n return String(i18next.t(entryIssueKey(issue), { defaultValue: issue.code, ...params }))\n}\n\n/** Where a row came from, in the screen's words - \"said in the conversation\" is the framework's, never the model's English. */\nfunction sourceText(source: { attachmentId?: string; where?: string; person?: boolean }): string {\n if (source.person) return t('source-person', 'said in the conversation')\n return source.where || source.attachmentId || ''\n}\n\n/** An enum value in the person's words when the target names its key - the code only when it does not. */\nfunction valueOf(column: { valueKeyPrefix?: string }, value: unknown): string {\n if (column.valueKeyPrefix && typeof value === 'string' && value) return String(i18next.t(`${column.valueKeyPrefix}${value.toLowerCase()}`, { defaultValue: value }))\n return shown(value)\n}\n\nfunction shown(value: unknown): string {\n if (value === undefined || value === null) return ''\n /* A reference: the record's name the server found, or the text that found none. */\n if (typeof value === 'object') return String((value as any).label ?? (value as any).text ?? (value as any).id ?? '')\n if (typeof value === 'number') return value.toLocaleString(i18next.language || undefined)\n if (typeof value === 'boolean') return value ? '✓' : '—'\n return String(value)\n}\n\n@customElement('ox-assistant-entry-review')\nexport class AssistantEntryReview extends LitElement {\n /* The inner scroll areas wear the standard scrollbar, as the dock does. */\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n margin: 4px 0 2px;\n font-size: 12px;\n color: var(--md-sys-color-on-surface);\n }\n\n .summary {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 10px;\n margin-bottom: 6px;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n .summary .blocked {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n\n .scroll {\n max-height: 320px;\n overflow: auto;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n\n table {\n width: 100%;\n border-collapse: collapse;\n }\n\n th,\n td {\n padding: 4px 6px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n text-align: start;\n vertical-align: top;\n white-space: nowrap;\n }\n\n th {\n position: sticky;\n top: 0;\n background: var(--md-sys-color-surface-container);\n font-weight: 600;\n }\n\n td.num {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n tr.blocked td {\n background: color-mix(in srgb, var(--md-sys-color-error) 6%, transparent);\n }\n\n td.bad {\n color: var(--md-sys-color-error);\n text-decoration: underline wavy var(--md-sys-color-error);\n text-underline-offset: 3px;\n }\n\n td.note {\n text-decoration: underline dotted var(--md-sys-color-outline);\n text-underline-offset: 3px;\n }\n\n .before {\n color: var(--md-sys-color-on-surface-variant);\n text-decoration: line-through;\n }\n\n .missing {\n color: var(--md-sys-color-error);\n font-style: italic;\n }\n\n .source {\n color: var(--md-sys-color-on-surface-variant);\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .row-issue {\n display: block;\n color: var(--md-sys-color-on-surface-variant);\n white-space: normal;\n }\n\n .row-issue.blocking {\n color: var(--md-sys-color-error);\n }\n\n .verified {\n color: var(--md-sys-color-primary);\n }\n\n .verified md-icon,\n .totals md-icon {\n --md-icon-size: 14px;\n vertical-align: -2px;\n }\n\n .totals {\n display: flex;\n flex-wrap: wrap;\n gap: 2px 12px;\n margin: 6px 0 0;\n font-variant-numeric: tabular-nums;\n }\n\n .totals .differs {\n color: var(--md-sys-color-error);\n font-weight: 600;\n }\n\n .totals .matches {\n color: var(--md-sys-color-primary);\n }\n\n .more,\n .check-failed {\n margin-top: 4px;\n color: var(--md-sys-color-on-surface-variant);\n }\n `\n ]\n\n @property({ attribute: false }) proposal: any\n\n /** The sum of each totalled field, beside the total the material prints - a missing row shows here without reading every row. */\n private renderTotals(totals: EntryTotalView[], columns: { name: string; labelKey?: string }[]) {\n if (!totals.length) return nothing\n const name = (field: string) => {\n const column = columns.find(one => one.name === field)\n return column?.labelKey ? i18next.t(column.labelKey) : field\n }\n return html`<div class=\"totals\" role=\"status\">\n ${totals.map(total => html`\n <span>${t('total', '{field} total {sum}', { field: name(total.field), sum: shown(total.all) })}</span>\n ${total.ready !== total.all ? html`<span>${t('total-ready', 'entered {sum}', { sum: shown(total.ready) })}</span>` : nothing}\n ${total.declared !== undefined\n ? html`<span>${total.declaredBy === 'file' ? t('total-declared-file', \"the file's total line {sum}\", { sum: shown(total.declared) }) : t('total-declared', 'the material says {sum}', { sum: shown(total.declared) })}${total.declaredFound === false ? ` (${t('total-not-found', 'not found in the file')})` : ''}</span>\n ${total.difference\n ? html`<span class=\"differs\">${t('total-differs', 'differs by {sum}', { sum: shown(total.difference) })}</span>`\n : html`<span class=\"matches\"><md-icon>task_alt</md-icon> ${t('total-matches', 'matches')}</span>`}`\n : nothing}\n `)}\n </div>`\n }\n\n /**\n * A 수정안 - each record by its name, each changing field as what it holds now and what it becomes, and how the\n * record was picked. The \"now\" is the server's reading, never the model's memory.\n */\n private renderChanges(p: any) {\n const columns: { name: string; type: string; labelKey?: string; valueKeyPrefix?: string }[] = p.columns ?? []\n const rows: any[] = p.rows ?? []\n const counts = p.counts ?? {}\n const via = (row: any) =>\n row.via === 'current' ? t('via-current', 'the record on screen') : row.via === 'selected' ? t('via-selected', 'selected on screen') : t('via-id', 'found in the conversation')\n return html`\n <div class=\"summary\" role=\"status\">\n <span>${t('changes', '{count} records', { count: counts.rows ?? rows.length })}</span>\n <span>${t('ready', '{count} ready', { count: counts.ready ?? 0 })}</span>\n ${counts.blocked ? html`<span class=\"blocked\">${t('not-changed', '{count} will not be changed', { count: counts.blocked })}</span>` : nothing}\n </div>\n <div class=\"scroll\">\n <table>\n <thead>\n <tr>\n <th>#</th>\n <th>${t('record', 'record')}</th>\n ${columns.map(column => html`<th>${column.labelKey ? i18next.t(column.labelKey) : column.name}</th>`)}\n <th>${t('picked', 'picked')}</th>\n </tr>\n </thead>\n <tbody>\n ${rows.slice(0, SHOWN).map(row => {\n const blocked = (row.issues ?? []).some((issue: EntryIssueView) => issue.blocking)\n const whole = entryCellIssues(row)\n return html`<tr class=${blocked ? 'blocked' : ''}>\n <td class=\"num\">${row.index + 1}</td>\n <td>${row.label || row.id}${whole.map(issue => html`<span class=\"row-issue ${issue.blocking ? 'blocking' : ''}\">${issueText(issue)}</span>`)}</td>\n ${columns.map(column => {\n const issues = entryCellIssues(row, column.name)\n const bad = issues.some(issue => issue.blocking)\n const changes = column.name in (row.values ?? {})\n return html`<td class=\"${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}\" title=${issues.map(issueText).join('\\n')}>\n ${changes ? html`<span class=\"before\">${valueOf(column, row.before?.[column.name]) || '—'}</span> → <strong>${valueOf(column, row.values[column.name]) || '—'}</strong>` : ''}\n </td>`\n })}\n <td class=\"source\">${via(row)}</td>\n </tr>`\n })}\n </tbody>\n </table>\n </div>\n `\n }\n\n render() {\n const p = this.proposal ?? {}\n if (p.op === 'update') return this.renderChanges(p)\n const columns: { name: string; type: string; required?: boolean; labelKey?: string; valueKeyPrefix?: string }[] = p.columns ?? []\n const rows: EntryRowView[] = p.rows ?? []\n const counts = p.counts ?? {}\n return html`\n <div class=\"summary\" role=\"status\">\n <span>${t('rows', '{count} rows', { count: counts.rows ?? rows.length })}</span>\n <span>${t('ready', '{count} ready', { count: counts.ready ?? 0 })}</span>\n ${counts.blocked ? html`<span class=\"blocked\">${t('blocked', '{count} will not be entered', { count: counts.blocked })}</span>` : nothing}\n ${counts.cut ? html`<span class=\"blocked\">${t('cut', '{count} left out over the limit', { count: counts.cut })}</span>` : nothing}\n ${counts.verified ? html`<span class=\"verified\"><md-icon>task_alt</md-icon> ${t('verified', '{count} found in their file', { count: counts.verified })}</span>` : nothing}\n ${counts.unmatched ? html`<span class=\"blocked\">${t('unmatched', '{count} with a value their file does not have', { count: counts.unmatched })}</span>` : nothing}\n </div>\n <div class=\"scroll\">\n <table>\n <thead>\n <tr>\n <th>#</th>\n ${columns.map(column => html`<th>${column.labelKey ? i18next.t(column.labelKey) : column.name}${column.required ? ' *' : ''}</th>`)}\n <th>${t('source', 'from')}</th>\n </tr>\n </thead>\n <tbody>\n ${rows.slice(0, SHOWN).map(row => {\n const blocked = (row.issues ?? []).some(issue => issue.blocking)\n const whole = entryCellIssues(row)\n return html`\n <tr class=${blocked ? 'blocked' : ''}>\n <td class=\"num\">${row.index + 1}</td>\n ${columns.map(column => {\n const issues = entryCellIssues(row, column.name)\n const bad = issues.some(issue => issue.blocking)\n const value = row.values?.[column.name]\n const text = valueOf(column, value)\n return html`<td\n class=\"${column.type === 'number' || column.type === 'integer' ? 'num' : ''} ${bad ? 'bad' : issues.length ? 'note' : ''}\"\n title=${issues.map(issueText).join('\\n')}\n >\n ${text || (issues.some(issue => issue.code === 'required') ? html`<span class=\"missing\">${t('missing', 'missing')}</span>` : '')}\n </td>`\n })}\n <td class=\"source\" title=${(row.sources ?? []).map(sourceText).join(' · ')}>\n ${row.evidence ? html`<span class=\"verified\" title=${t('evidence', 'found on line {line} of the file', { line: row.evidence.line })}><md-icon>task_alt</md-icon></span> ` : nothing}\n ${(row.sources ?? []).map(sourceText).join(' · ')}\n ${whole\n .filter(issue => issue.code !== 'no-source')\n .map(issue => html`<span class=\"row-issue ${issue.blocking ? 'blocking' : ''}\">${issueText(issue)}</span>`)}\n ${whole.some(issue => issue.code === 'no-source') ? html`<span class=\"row-issue\">${issueText({ code: 'no-source', blocking: false })}</span>` : nothing}\n </td>\n </tr>\n `\n })}\n </tbody>\n </table>\n </div>\n ${this.renderTotals(p.totals ?? [], columns)}\n ${rows.length > SHOWN ? html`<div class=\"more\">${t('more-rows', '{count} more rows are not shown - they are entered too.', { count: rows.length - SHOWN })}</div>` : nothing}\n ${p.checkFailed ? html`<div class=\"check-failed\">${t('check-failed', 'the application could not check these rows; they are checked again when entered.')}</div>` : nothing}\n `\n }\n}\n"]}
@@ -2,7 +2,7 @@ import '@material/web/icon/icon.js';
2
2
  import { LitElement, type TemplateResult } from 'lit';
3
3
  import { type WorkspaceView } from '../utils/workspace.js';
4
4
  export declare class AssistantWorkspaceView extends LitElement {
5
- static styles: import("lit").CSSResult;
5
+ static styles: import("lit").CSSResult[];
6
6
  workspace?: WorkspaceView;
7
7
  private sorts;
8
8
  private asTable;
@@ -11,6 +11,7 @@ import { LitElement, css, html, nothing, svg } from 'lit';
11
11
  import { customElement, property, state } from 'lit/decorators.js';
12
12
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
13
13
  import { i18next } from '@operato/i18n';
14
+ import { ScrollbarStyles } from '@operato/styles';
14
15
  import { renderMarkdown } from './markdown.js';
15
16
  import { sortRows, tableCsv } from '../utils/workspace.js';
16
17
  const LIGHT = ['#2a78d6', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#008300', '#4a3aa7', '#e34948'];
@@ -33,7 +34,10 @@ let AssistantWorkspaceView = class AssistantWorkspaceView extends LitElement {
33
34
  this.asTable = new Set();
34
35
  this.dark = false;
35
36
  }
36
- static { this.styles = css `
37
+ /* The inner scroll areas wear the standard scrollbar, as the dock does. */
38
+ static { this.styles = [
39
+ ScrollbarStyles,
40
+ css `
37
41
  :host {
38
42
  display: block;
39
43
  color: var(--md-sys-color-on-surface);
@@ -239,7 +243,8 @@ let AssistantWorkspaceView = class AssistantWorkspaceView extends LitElement {
239
243
  .markdown :first-child {
240
244
  margin-top: 0;
241
245
  }
242
- `; }
246
+ `
247
+ ]; }
243
248
  connectedCallback() {
244
249
  super.connectedCallback();
245
250
  this.dark = document.body.classList.contains('dark');
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-view.js","sourceRoot":"","sources":["../../client/components/workspace-view.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The blocks of an AI 작업판, drawn (operato-application/design/ai/workspace.md §3).\n *\n * Every kind is drawn here - the model writes no HTML. Charts use the eight validated categorical colours in\n * a fixed order (dataviz reference palette, light and dark each checked); three of the light ones sit under\n * 3:1 against the surface, so every chart carries its legend (two series or more) and a table view.\n */\nimport '@material/web/icon/icon.js'\n\nimport { LitElement, css, html, nothing, svg, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { renderMarkdown } from './markdown.js'\nimport { sortRows, tableCsv, type WorkspaceBlockView, type WorkspaceView } from '../utils/workspace.js'\n\nconst LIGHT = ['#2a78d6', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#008300', '#4a3aa7', '#e34948']\nconst DARK = ['#3987e5', '#d95926', '#199e70', '#c98500', '#d55181', '#008300', '#9085e9', '#e66767']\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n String(i18next.t(`ai-assistant.workspace.${key}`, { defaultValue, ...options }))\n\nconst shown = (value: unknown) =>\n value === null || value === undefined ? '' : typeof value === 'number' ? value.toLocaleString(i18next.language || undefined) : String(value)\n\n/** Four round steps from zero to above the largest value. */\nfunction ticksFor(max: number): number[] {\n if (!(max > 0)) return [0, 1]\n const raw = max / 4\n const power = 10 ** Math.floor(Math.log10(raw))\n const step = [1, 2, 2.5, 5, 10].map(one => one * power).find(one => one >= raw) ?? raw\n return Array.from({ length: Math.ceil(max / step) + 1 }, (_, i) => Math.round(i * step * 1e6) / 1e6)\n}\n\n@customElement('ox-assistant-workspace-view')\nexport class AssistantWorkspaceView extends LitElement {\n static styles = css`\n :host {\n display: block;\n color: var(--md-sys-color-on-surface);\n font-size: 14px;\n line-height: 1.5;\n }\n\n section {\n margin: 0 0 20px;\n }\n\n h1,\n h2,\n h3 {\n margin: 24px 0 8px;\n line-height: 1.3;\n }\n\n h1 {\n font-size: 22px;\n }\n\n h2 {\n font-size: 18px;\n }\n\n h3 {\n font-size: 15px;\n }\n\n .metrics {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));\n gap: 10px;\n }\n\n .metric {\n padding: 12px 14px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 10px;\n background: var(--md-sys-color-surface-container-lowest);\n }\n\n .metric .label {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .metric .value {\n font-size: 22px;\n font-weight: 650;\n font-variant-numeric: tabular-nums;\n }\n\n .metric .note {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .bar {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .bar .caption {\n flex: 1;\n }\n\n .bar button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .bar md-icon {\n --md-icon-size: 16px;\n }\n\n .scroll {\n max-height: 560px;\n overflow: auto;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n }\n\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n th,\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n text-align: start;\n white-space: nowrap;\n }\n\n th {\n position: sticky;\n top: 0;\n background: var(--md-sys-color-surface-container);\n font-weight: 600;\n }\n\n th button {\n border: 0;\n padding: 0;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n td.num,\n th.num {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n .chart svg {\n width: 100%;\n height: auto;\n display: block;\n }\n\n .chart .grid {\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 1;\n }\n\n .chart .tick {\n fill: var(--md-sys-color-on-surface-variant);\n font-size: 11px;\n }\n\n .legend {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n margin-top: 6px;\n font-size: 12px;\n }\n\n .legend .swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin-inline-end: 5px;\n border-radius: 3px;\n vertical-align: -1px;\n }\n\n .steps {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n .steps li {\n display: flex;\n gap: 8px;\n padding: 4px 0;\n }\n\n .steps md-icon {\n --md-icon-size: 18px;\n }\n\n .steps .done md-icon {\n color: var(--md-sys-color-primary);\n }\n\n .steps .failed md-icon {\n color: var(--md-sys-color-error);\n }\n\n .steps .note {\n display: block;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .unreadable,\n .cut {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n font-style: italic;\n }\n\n .markdown :first-child {\n margin-top: 0;\n }\n `\n\n @property({ attribute: false }) workspace?: WorkspaceView\n\n @state() private sorts: Record<number, { column: string; direction: 'asc' | 'desc' }> = {}\n @state() private asTable = new Set<number>()\n @state() private dark = false\n\n private themeWatch?: MutationObserver\n\n connectedCallback() {\n super.connectedCallback()\n this.dark = document.body.classList.contains('dark')\n this.themeWatch = new MutationObserver(() => (this.dark = document.body.classList.contains('dark')))\n this.themeWatch.observe(document.body, { attributes: true, attributeFilter: ['class'] })\n }\n\n disconnectedCallback() {\n this.themeWatch?.disconnect()\n super.disconnectedCallback()\n }\n\n render() {\n const blocks = this.workspace?.blocks ?? []\n return html`${blocks.map((block, i) => this.block(block, i))}`\n }\n\n private block(block: WorkspaceBlockView, i: number): TemplateResult {\n switch (block.kind) {\n case 'heading':\n return block.level === 1 ? html`<h1>${block.text}</h1>` : block.level === 3 ? html`<h3>${block.text}</h3>` : html`<h2>${block.text}</h2>`\n case 'text':\n return html`<section class=\"markdown\">${unsafeHTML(renderMarkdown(block.markdown))}</section>`\n case 'metrics':\n return html`<section class=\"metrics\">\n ${(block.items ?? []).map(\n (one: any) => html`<div class=\"metric\">\n <div class=\"label\">${one.label}</div>\n <div class=\"value\">${shown(one.value)}${one.unit ? html` <small>${one.unit}</small>` : nothing}</div>\n ${one.note ? html`<div class=\"note\">${one.note}</div>` : nothing}\n </div>`\n )}\n </section>`\n case 'table':\n return this.table(block, i)\n case 'chart':\n return this.chart(block, i)\n case 'steps':\n return html`<section>\n <ol class=\"steps\">\n ${(block.items ?? []).map(\n (one: any) => html`<li class=${one.state}>\n <md-icon>${{ done: 'check_circle', doing: 'progress_activity', failed: 'error', todo: 'radio_button_unchecked' }[one.state as string] ?? 'radio_button_unchecked'}</md-icon>\n <span>${one.label}${one.note ? html`<span class=\"note\">${one.note}</span>` : nothing}</span>\n </li>`\n )}\n </ol>\n </section>`\n default:\n return html`<section class=\"unreadable\">${t('unreadable', 'a block that cannot be drawn ({reason})', { reason: block.reason ?? block.kind })}</section>`\n }\n }\n\n private table(block: WorkspaceBlockView, i: number): TemplateResult {\n const columns: { name: string; label?: string; type?: string }[] = block.columns ?? []\n const sort = this.sorts[i]\n const rows = sort ? sortRows(block.rows ?? [], sort.column, sort.direction, i18next.language) : block.rows ?? []\n const toggle = (column: string) =>\n (this.sorts = { ...this.sorts, [i]: { column, direction: sort?.column === column && sort.direction === 'asc' ? 'desc' : 'asc' } })\n return html`<section>\n <div class=\"bar\">\n <span class=\"caption\">${block.caption ?? ''} · ${t('rows', '{count} rows', { count: rows.length })}</span>\n <button @click=${() => this.download(block.caption || this.workspace?.title || 'table', columns, rows)}>\n <md-icon>download</md-icon>${t('csv', 'CSV')}\n </button>\n </div>\n ${this.tableOf(columns, rows, sort, toggle)}\n ${block.cut ? html`<div class=\"cut\">${t('cut', '{count} more rows were left out over the limit.', { count: block.cut })}</div>` : nothing}\n </section>`\n }\n\n private tableOf(columns: { name: string; label?: string; type?: string }[], rows: Record<string, any>[], sort?: { column: string; direction: string }, toggle?: (column: string) => void) {\n return html`<div class=\"scroll\">\n <table>\n <thead>\n <tr>\n ${columns.map(\n column => html`<th class=${column.type === 'number' ? 'num' : ''} aria-sort=${sort?.column === column.name ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none'}>\n ${toggle\n ? html`<button @click=${() => toggle(column.name)}>${column.label || column.name}${sort?.column === column.name ? (sort.direction === 'asc' ? ' ▲' : ' ▼') : ''}</button>`\n : column.label || column.name}\n </th>`\n )}\n </tr>\n </thead>\n <tbody>\n ${rows.map(row => html`<tr>${columns.map(column => html`<td class=${column.type === 'number' || typeof row[column.name] === 'number' ? 'num' : ''}>${shown(row[column.name])}</td>`)}</tr>`)}\n </tbody>\n </table>\n </div>`\n }\n\n private chart(block: WorkspaceBlockView, i: number): TemplateResult {\n const colours = this.dark ? DARK : LIGHT\n const x: string[] = block.x ?? []\n const series: { name: string; other?: boolean; values: (number | null)[] }[] = block.series ?? []\n const name = (one: { name: string; other?: boolean }) => (one.other ? t('other', 'other') : one.name)\n const showingTable = this.asTable.has(i)\n const flip = () => {\n const next = new Set(this.asTable)\n next.has(i) ? next.delete(i) : next.add(i)\n this.asTable = next\n }\n\n const bar = html`<div class=\"bar\">\n <span class=\"caption\">${block.caption ?? ''}${block.unit ? ` (${block.unit})` : ''}</span>\n <button @click=${flip}><md-icon>${showingTable ? 'bar_chart' : 'table_rows'}</md-icon>${showingTable ? t('as-chart', 'chart') : t('as-table', 'table')}</button>\n </div>`\n\n if (showingTable) {\n const columns = [{ name: '_x', label: '' }, ...series.map((one, s) => ({ name: `s${s}`, label: name(one), type: 'number' }))]\n const rows = x.map((label, j) => Object.fromEntries([['_x', label], ...series.map((one, s) => [`s${s}`, one.values[j]])]))\n return html`<section class=\"chart\">${bar}${this.tableOf(columns, rows)}</section>`\n }\n\n const W = 640\n const H = 240\n const left = 56\n const bottom = 28\n const plotW = W - left - 8\n const plotH = H - bottom - 10\n const values = series.flatMap(one => one.values).filter((v): v is number => typeof v === 'number')\n const min = Math.min(0, ...values)\n const ticks = ticksFor(Math.max(...values, 0) - min).map(tick => tick + min)\n const top = ticks[ticks.length - 1] || 1\n const y = (v: number) => 10 + plotH - ((v - min) / (top - min || 1)) * plotH\n const band = plotW / Math.max(1, x.length)\n const everyNth = Math.max(1, Math.ceil(x.length / 12))\n\n const marks =\n block.chart === 'line'\n ? series.map((one, s) => {\n const points = one.values.map((v, j) => (typeof v === 'number' ? [left + band * (j + 0.5), y(v)] : null))\n const path = points.reduce((d, p, j) => (p ? `${d}${d && points[j - 1] ? 'L' : 'M'}${p[0]},${p[1]}` : d), '')\n return svg`<path d=${path} fill=\"none\" stroke=${colours[s % 8]} stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"></path>\n ${points.map((p, j) =>\n p ? svg`<circle cx=${p[0]} cy=${p[1]} r=\"4\" fill=${colours[s % 8]} stroke=\"var(--md-sys-color-surface)\" stroke-width=\"2\"><title>${name(one)} · ${x[j]}: ${shown(one.values[j])}</title></circle>` : nothing\n )}`\n })\n : series.map((one, s) => {\n const inner = band * 0.7\n const width = Math.max(2, inner / series.length - 2)\n return one.values.map((v, j) => {\n if (typeof v !== 'number') return nothing\n const x0 = left + band * j + (band - inner) / 2 + s * (width + 2)\n const y0 = Math.min(y(v), y(0))\n const h = Math.max(1, Math.abs(y(v) - y(0)))\n return svg`<rect x=${x0} y=${y0} width=${width} height=${h} rx=\"3\" fill=${colours[s % 8]}><title>${name(one)} · ${x[j]}: ${shown(v)}</title></rect>`\n })\n })\n\n return html`<section class=\"chart\" role=\"img\" aria-label=${block.caption || series.map(name).join(', ')}>\n ${bar}\n <svg viewBox=\"0 0 ${W} ${H}\">\n ${ticks.map(tick => svg`<line class=\"grid\" x1=${left} x2=${W - 8} y1=${y(tick)} y2=${y(tick)}></line><text class=\"tick\" x=${left - 6} y=${y(tick) + 4} text-anchor=\"end\">${shown(tick)}</text>`)}\n ${marks}\n ${x.map((label, j) => (j % everyNth ? nothing : svg`<text class=\"tick\" x=${left + band * (j + 0.5)} y=${H - 8} text-anchor=\"middle\">${label}</text>`))}\n </svg>\n ${series.length > 1\n ? html`<div class=\"legend\">${series.map((one, s) => html`<span><span class=\"swatch\" style=\"background:${colours[s % 8]}\"></span>${name(one)}</span>`)}</div>`\n : nothing}\n </section>`\n }\n\n private download(title: string, columns: { name: string; label?: string }[], rows: Record<string, any>[]) {\n const blob = new Blob([tableCsv(columns, rows)], { type: 'text/csv;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `${title.replace(/[\\\\/:*?\"<>|]+/g, '-').slice(0, 80)}.csv`\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n}\n"]}
1
+ {"version":3,"file":"workspace-view.js","sourceRoot":"","sources":["../../client/components/workspace-view.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * The blocks of an AI 작업판, drawn (operato-application/design/ai/workspace.md §3).\n *\n * Every kind is drawn here - the model writes no HTML. Charts use the eight validated categorical colours in\n * a fixed order (dataviz reference palette, light and dark each checked); three of the light ones sit under\n * 3:1 against the surface, so every chart carries its legend (two series or more) and a table view.\n */\nimport '@material/web/icon/icon.js'\n\nimport { LitElement, css, html, nothing, svg, type TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js'\n\nimport { i18next } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { renderMarkdown } from './markdown.js'\nimport { sortRows, tableCsv, type WorkspaceBlockView, type WorkspaceView } from '../utils/workspace.js'\n\nconst LIGHT = ['#2a78d6', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#008300', '#4a3aa7', '#e34948']\nconst DARK = ['#3987e5', '#d95926', '#199e70', '#c98500', '#d55181', '#008300', '#9085e9', '#e66767']\n\nconst t = (key: string, defaultValue: string, options: Record<string, unknown> = {}) =>\n String(i18next.t(`ai-assistant.workspace.${key}`, { defaultValue, ...options }))\n\nconst shown = (value: unknown) =>\n value === null || value === undefined ? '' : typeof value === 'number' ? value.toLocaleString(i18next.language || undefined) : String(value)\n\n/** Four round steps from zero to above the largest value. */\nfunction ticksFor(max: number): number[] {\n if (!(max > 0)) return [0, 1]\n const raw = max / 4\n const power = 10 ** Math.floor(Math.log10(raw))\n const step = [1, 2, 2.5, 5, 10].map(one => one * power).find(one => one >= raw) ?? raw\n return Array.from({ length: Math.ceil(max / step) + 1 }, (_, i) => Math.round(i * step * 1e6) / 1e6)\n}\n\n@customElement('ox-assistant-workspace-view')\nexport class AssistantWorkspaceView extends LitElement {\n /* The inner scroll areas wear the standard scrollbar, as the dock does. */\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n color: var(--md-sys-color-on-surface);\n font-size: 14px;\n line-height: 1.5;\n }\n\n section {\n margin: 0 0 20px;\n }\n\n h1,\n h2,\n h3 {\n margin: 24px 0 8px;\n line-height: 1.3;\n }\n\n h1 {\n font-size: 22px;\n }\n\n h2 {\n font-size: 18px;\n }\n\n h3 {\n font-size: 15px;\n }\n\n .metrics {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));\n gap: 10px;\n }\n\n .metric {\n padding: 12px 14px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 10px;\n background: var(--md-sys-color-surface-container-lowest);\n }\n\n .metric .label {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .metric .value {\n font-size: 22px;\n font-weight: 650;\n font-variant-numeric: tabular-nums;\n }\n\n .metric .note {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .bar {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .bar .caption {\n flex: 1;\n }\n\n .bar button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n .bar md-icon {\n --md-icon-size: 16px;\n }\n\n .scroll {\n max-height: 560px;\n overflow: auto;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n }\n\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n\n th,\n td {\n padding: 6px 10px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n text-align: start;\n white-space: nowrap;\n }\n\n th {\n position: sticky;\n top: 0;\n background: var(--md-sys-color-surface-container);\n font-weight: 600;\n }\n\n th button {\n border: 0;\n padding: 0;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n }\n\n td.num,\n th.num {\n text-align: end;\n font-variant-numeric: tabular-nums;\n }\n\n .chart svg {\n width: 100%;\n height: auto;\n display: block;\n }\n\n .chart .grid {\n stroke: var(--md-sys-color-outline-variant);\n stroke-width: 1;\n }\n\n .chart .tick {\n fill: var(--md-sys-color-on-surface-variant);\n font-size: 11px;\n }\n\n .legend {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 14px;\n margin-top: 6px;\n font-size: 12px;\n }\n\n .legend .swatch {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin-inline-end: 5px;\n border-radius: 3px;\n vertical-align: -1px;\n }\n\n .steps {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n .steps li {\n display: flex;\n gap: 8px;\n padding: 4px 0;\n }\n\n .steps md-icon {\n --md-icon-size: 18px;\n }\n\n .steps .done md-icon {\n color: var(--md-sys-color-primary);\n }\n\n .steps .failed md-icon {\n color: var(--md-sys-color-error);\n }\n\n .steps .note {\n display: block;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n }\n\n .unreadable,\n .cut {\n color: var(--md-sys-color-on-surface-variant);\n font-size: 12px;\n font-style: italic;\n }\n\n .markdown :first-child {\n margin-top: 0;\n }\n `\n ]\n\n @property({ attribute: false }) workspace?: WorkspaceView\n\n @state() private sorts: Record<number, { column: string; direction: 'asc' | 'desc' }> = {}\n @state() private asTable = new Set<number>()\n @state() private dark = false\n\n private themeWatch?: MutationObserver\n\n connectedCallback() {\n super.connectedCallback()\n this.dark = document.body.classList.contains('dark')\n this.themeWatch = new MutationObserver(() => (this.dark = document.body.classList.contains('dark')))\n this.themeWatch.observe(document.body, { attributes: true, attributeFilter: ['class'] })\n }\n\n disconnectedCallback() {\n this.themeWatch?.disconnect()\n super.disconnectedCallback()\n }\n\n render() {\n const blocks = this.workspace?.blocks ?? []\n return html`${blocks.map((block, i) => this.block(block, i))}`\n }\n\n private block(block: WorkspaceBlockView, i: number): TemplateResult {\n switch (block.kind) {\n case 'heading':\n return block.level === 1 ? html`<h1>${block.text}</h1>` : block.level === 3 ? html`<h3>${block.text}</h3>` : html`<h2>${block.text}</h2>`\n case 'text':\n return html`<section class=\"markdown\">${unsafeHTML(renderMarkdown(block.markdown))}</section>`\n case 'metrics':\n return html`<section class=\"metrics\">\n ${(block.items ?? []).map(\n (one: any) => html`<div class=\"metric\">\n <div class=\"label\">${one.label}</div>\n <div class=\"value\">${shown(one.value)}${one.unit ? html` <small>${one.unit}</small>` : nothing}</div>\n ${one.note ? html`<div class=\"note\">${one.note}</div>` : nothing}\n </div>`\n )}\n </section>`\n case 'table':\n return this.table(block, i)\n case 'chart':\n return this.chart(block, i)\n case 'steps':\n return html`<section>\n <ol class=\"steps\">\n ${(block.items ?? []).map(\n (one: any) => html`<li class=${one.state}>\n <md-icon>${{ done: 'check_circle', doing: 'progress_activity', failed: 'error', todo: 'radio_button_unchecked' }[one.state as string] ?? 'radio_button_unchecked'}</md-icon>\n <span>${one.label}${one.note ? html`<span class=\"note\">${one.note}</span>` : nothing}</span>\n </li>`\n )}\n </ol>\n </section>`\n default:\n return html`<section class=\"unreadable\">${t('unreadable', 'a block that cannot be drawn ({reason})', { reason: block.reason ?? block.kind })}</section>`\n }\n }\n\n private table(block: WorkspaceBlockView, i: number): TemplateResult {\n const columns: { name: string; label?: string; type?: string }[] = block.columns ?? []\n const sort = this.sorts[i]\n const rows = sort ? sortRows(block.rows ?? [], sort.column, sort.direction, i18next.language) : block.rows ?? []\n const toggle = (column: string) =>\n (this.sorts = { ...this.sorts, [i]: { column, direction: sort?.column === column && sort.direction === 'asc' ? 'desc' : 'asc' } })\n return html`<section>\n <div class=\"bar\">\n <span class=\"caption\">${block.caption ?? ''} · ${t('rows', '{count} rows', { count: rows.length })}</span>\n <button @click=${() => this.download(block.caption || this.workspace?.title || 'table', columns, rows)}>\n <md-icon>download</md-icon>${t('csv', 'CSV')}\n </button>\n </div>\n ${this.tableOf(columns, rows, sort, toggle)}\n ${block.cut ? html`<div class=\"cut\">${t('cut', '{count} more rows were left out over the limit.', { count: block.cut })}</div>` : nothing}\n </section>`\n }\n\n private tableOf(columns: { name: string; label?: string; type?: string }[], rows: Record<string, any>[], sort?: { column: string; direction: string }, toggle?: (column: string) => void) {\n return html`<div class=\"scroll\">\n <table>\n <thead>\n <tr>\n ${columns.map(\n column => html`<th class=${column.type === 'number' ? 'num' : ''} aria-sort=${sort?.column === column.name ? (sort.direction === 'asc' ? 'ascending' : 'descending') : 'none'}>\n ${toggle\n ? html`<button @click=${() => toggle(column.name)}>${column.label || column.name}${sort?.column === column.name ? (sort.direction === 'asc' ? ' ▲' : ' ▼') : ''}</button>`\n : column.label || column.name}\n </th>`\n )}\n </tr>\n </thead>\n <tbody>\n ${rows.map(row => html`<tr>${columns.map(column => html`<td class=${column.type === 'number' || typeof row[column.name] === 'number' ? 'num' : ''}>${shown(row[column.name])}</td>`)}</tr>`)}\n </tbody>\n </table>\n </div>`\n }\n\n private chart(block: WorkspaceBlockView, i: number): TemplateResult {\n const colours = this.dark ? DARK : LIGHT\n const x: string[] = block.x ?? []\n const series: { name: string; other?: boolean; values: (number | null)[] }[] = block.series ?? []\n const name = (one: { name: string; other?: boolean }) => (one.other ? t('other', 'other') : one.name)\n const showingTable = this.asTable.has(i)\n const flip = () => {\n const next = new Set(this.asTable)\n next.has(i) ? next.delete(i) : next.add(i)\n this.asTable = next\n }\n\n const bar = html`<div class=\"bar\">\n <span class=\"caption\">${block.caption ?? ''}${block.unit ? ` (${block.unit})` : ''}</span>\n <button @click=${flip}><md-icon>${showingTable ? 'bar_chart' : 'table_rows'}</md-icon>${showingTable ? t('as-chart', 'chart') : t('as-table', 'table')}</button>\n </div>`\n\n if (showingTable) {\n const columns = [{ name: '_x', label: '' }, ...series.map((one, s) => ({ name: `s${s}`, label: name(one), type: 'number' }))]\n const rows = x.map((label, j) => Object.fromEntries([['_x', label], ...series.map((one, s) => [`s${s}`, one.values[j]])]))\n return html`<section class=\"chart\">${bar}${this.tableOf(columns, rows)}</section>`\n }\n\n const W = 640\n const H = 240\n const left = 56\n const bottom = 28\n const plotW = W - left - 8\n const plotH = H - bottom - 10\n const values = series.flatMap(one => one.values).filter((v): v is number => typeof v === 'number')\n const min = Math.min(0, ...values)\n const ticks = ticksFor(Math.max(...values, 0) - min).map(tick => tick + min)\n const top = ticks[ticks.length - 1] || 1\n const y = (v: number) => 10 + plotH - ((v - min) / (top - min || 1)) * plotH\n const band = plotW / Math.max(1, x.length)\n const everyNth = Math.max(1, Math.ceil(x.length / 12))\n\n const marks =\n block.chart === 'line'\n ? series.map((one, s) => {\n const points = one.values.map((v, j) => (typeof v === 'number' ? [left + band * (j + 0.5), y(v)] : null))\n const path = points.reduce((d, p, j) => (p ? `${d}${d && points[j - 1] ? 'L' : 'M'}${p[0]},${p[1]}` : d), '')\n return svg`<path d=${path} fill=\"none\" stroke=${colours[s % 8]} stroke-width=\"2\" stroke-linejoin=\"round\" stroke-linecap=\"round\"></path>\n ${points.map((p, j) =>\n p ? svg`<circle cx=${p[0]} cy=${p[1]} r=\"4\" fill=${colours[s % 8]} stroke=\"var(--md-sys-color-surface)\" stroke-width=\"2\"><title>${name(one)} · ${x[j]}: ${shown(one.values[j])}</title></circle>` : nothing\n )}`\n })\n : series.map((one, s) => {\n const inner = band * 0.7\n const width = Math.max(2, inner / series.length - 2)\n return one.values.map((v, j) => {\n if (typeof v !== 'number') return nothing\n const x0 = left + band * j + (band - inner) / 2 + s * (width + 2)\n const y0 = Math.min(y(v), y(0))\n const h = Math.max(1, Math.abs(y(v) - y(0)))\n return svg`<rect x=${x0} y=${y0} width=${width} height=${h} rx=\"3\" fill=${colours[s % 8]}><title>${name(one)} · ${x[j]}: ${shown(v)}</title></rect>`\n })\n })\n\n return html`<section class=\"chart\" role=\"img\" aria-label=${block.caption || series.map(name).join(', ')}>\n ${bar}\n <svg viewBox=\"0 0 ${W} ${H}\">\n ${ticks.map(tick => svg`<line class=\"grid\" x1=${left} x2=${W - 8} y1=${y(tick)} y2=${y(tick)}></line><text class=\"tick\" x=${left - 6} y=${y(tick) + 4} text-anchor=\"end\">${shown(tick)}</text>`)}\n ${marks}\n ${x.map((label, j) => (j % everyNth ? nothing : svg`<text class=\"tick\" x=${left + band * (j + 0.5)} y=${H - 8} text-anchor=\"middle\">${label}</text>`))}\n </svg>\n ${series.length > 1\n ? html`<div class=\"legend\">${series.map((one, s) => html`<span><span class=\"swatch\" style=\"background:${colours[s % 8]}\"></span>${name(one)}</span>`)}</div>`\n : nothing}\n </section>`\n }\n\n private download(title: string, columns: { name: string; label?: string }[], rows: Record<string, any>[]) {\n const blob = new Blob([tableCsv(columns, rows)], { type: 'text/csv;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = `${title.replace(/[\\\\/:*?\"<>|]+/g, '-').slice(0, 80)}.csv`\n link.click()\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n}\n"]}