@table-bro/mcp 0.0.0-stage → 1.2.0

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -2
  3. package/dist/docs/docs.d.ts +21 -0
  4. package/dist/docs/docs.js +103 -0
  5. package/dist/docs/pages.d.ts +28 -0
  6. package/dist/docs/pages.js +2734 -0
  7. package/dist/docs/topics.d.ts +11 -0
  8. package/dist/docs/topics.js +410 -0
  9. package/dist/extension.d.ts +23 -0
  10. package/dist/extension.js +1 -0
  11. package/dist/generate/angular.d.ts +26 -0
  12. package/dist/generate/angular.js +241 -0
  13. package/dist/generate/cells.d.ts +22 -0
  14. package/dist/generate/cells.js +157 -0
  15. package/dist/generate/code.d.ts +48 -0
  16. package/dist/generate/code.js +153 -0
  17. package/dist/generate/config.d.ts +16 -0
  18. package/dist/generate/config.js +95 -0
  19. package/dist/generate/fillings.d.ts +49 -0
  20. package/dist/generate/fillings.js +130 -0
  21. package/dist/generate/filters.d.ts +36 -0
  22. package/dist/generate/filters.js +254 -0
  23. package/dist/generate/generate-code.d.ts +34 -0
  24. package/dist/generate/generate-code.js +59 -0
  25. package/dist/generate/names.d.ts +52 -0
  26. package/dist/generate/names.js +93 -0
  27. package/dist/generate/rows.d.ts +5 -0
  28. package/dist/generate/rows.js +22 -0
  29. package/dist/generate/scaffold.d.ts +35 -0
  30. package/dist/generate/scaffold.js +62 -0
  31. package/dist/generate/service.d.ts +16 -0
  32. package/dist/generate/service.js +320 -0
  33. package/dist/generate/setup.d.ts +34 -0
  34. package/dist/generate/setup.js +158 -0
  35. package/dist/generate/vanilla.d.ts +10 -0
  36. package/dist/generate/vanilla.js +160 -0
  37. package/dist/index.d.ts +14 -0
  38. package/dist/index.js +10 -0
  39. package/dist/project/inspect-project.d.ts +53 -0
  40. package/dist/project/inspect-project.js +223 -0
  41. package/dist/server/main.d.ts +2 -0
  42. package/dist/server/main.js +8 -0
  43. package/dist/server/server.d.ts +17 -0
  44. package/dist/server/server.js +212 -0
  45. package/dist/spec/builtin-specs.d.ts +13 -0
  46. package/dist/spec/builtin-specs.js +167 -0
  47. package/dist/spec/css-variables.d.ts +22 -0
  48. package/dist/spec/css-variables.js +54 -0
  49. package/dist/spec/schema.d.ts +19 -0
  50. package/dist/spec/schema.js +278 -0
  51. package/dist/spec/skin-config.d.ts +30 -0
  52. package/dist/spec/skin-config.js +67 -0
  53. package/dist/spec/spec.d.ts +157 -0
  54. package/dist/spec/spec.js +2 -0
  55. package/dist/spec/validate-spec.d.ts +54 -0
  56. package/dist/spec/validate-spec.js +979 -0
  57. package/dist/version.d.ts +2 -0
  58. package/dist/version.js +2 -0
  59. package/package.json +71 -4
@@ -0,0 +1,11 @@
1
+ export interface BroDocTopic {
2
+ name: string;
3
+ title: string;
4
+ summary: string;
5
+ text: () => string;
6
+ }
7
+ /**
8
+ * The topics of the reference. The texts that the package knows as data, the setup, the spec, the CSS variables,
9
+ * the settings of the skins and the scaffolds of the cells, are made from that data, so they cannot drift from it.
10
+ */
11
+ export declare const topics: BroDocTopic[];
@@ -0,0 +1,410 @@
1
+ import { cellCode } from '../generate/cells.js';
2
+ import { setupSteps, setupText } from '../generate/setup.js';
3
+ import { builtinSpecs } from '../spec/builtin-specs.js';
4
+ import { cssVariables, excludedCssVariables } from '../spec/css-variables.js';
5
+ import { specSchema } from '../spec/schema.js';
6
+ import { skinCallbacks, skinConfigShape, skinElementContracts } from '../spec/skin-config.js';
7
+ import { docPageMarkdown, docPages } from './pages.js';
8
+ const pairs = [['angular', 'mat'], ['angular', 'bs'], ['angular', 'nv'], ['vanilla', 'bs'], ['vanilla', 'nv']];
9
+ /**
10
+ * The topics of the reference. The texts that the package knows as data, the setup, the spec, the CSS variables,
11
+ * the settings of the skins and the scaffolds of the cells, are made from that data, so they cannot drift from it.
12
+ */
13
+ export const topics = [
14
+ {
15
+ name: 'basics',
16
+ title: 'How Table, Bro works',
17
+ summary: 'The facts a model cannot guess: the element, configure, skins, cells, the one event, the server mode',
18
+ text: () => `
19
+ - Table, Bro is a Web Components table engine on top of existing UI components: the table is the Custom Element \`<table-bro>\`, and a skin gives it the controls and the look of a UI library. Its columns, rows and config come once, in \`configure({cols, rows, conf})\`. New columns or a new config make a new table; rows change with \`replaceRows\` and \`appendRows\`, the loading and the failure with \`setState\`.
20
+ - A skin is required: \`config.skin\` is \`mat\` (Angular Material: pixel-perfect Material data tables for Angular), \`bs\` (Bootstrap 5) or \`nv\` (native HTML, no library). The import of a vanilla skin package registers it, \`provideTableBroMatSkin()\` registers the skin mat; \`configure\` throws without a registered skin. The table needs the CSS of the core and of the skin.
21
+ - The content of a custom cell is a Custom Element: the column names its tag in \`component.selector\`. The table creates it for every row and sets the properties \`col\` and \`row\`, the objects of the application.
22
+ - The tags of the ready elements depend on the skin: the tags are \`bro-mat-tags\`, \`bro-bs-tags\` or \`bro-nv-tags\`.
23
+ - Every action comes in one event \`bro-action\` with \`detail = {action, selector, data}\`. A custom cell sends its actions through the event named in \`component.event\`, and the table gives the page \`data = {col, row}\`.
24
+ - In the server mode the table tells what to load: a \`BroQuery\` in the action \`bro-filters-server-loaded\`, the output \`query\` of the Angular wrapper.
25
+ - All body rows have one fixed height, the CSS variable \`--bro-row-height\` of the skin, not the content.
26
+ - All loaded rows are in the DOM: there is no virtualization.
27
+ - The table creates the cells also to measure the widths of the columns, for a row of the longest values: a cell has no side effects when it connects.
28
+ - The filters of the application live outside of the table, in the Filter Drawer next to it. The table itself searches the string fields of its columns; other filters go through \`config.filtersTransform\` in the local mode.`
29
+ },
30
+ {
31
+ name: 'fillings',
32
+ title: 'The fillings of generate_code',
33
+ summary: 'The format of the fillings: the templates of the cells, the rules of the custom filters and the bodies of the functions of the skin config',
34
+ text: () => `
35
+ \`generate_code\` writes the scaffolds, and the fillings are the parts that you write. Without them the files keep TODOs. The format:
36
+
37
+ \`\`\`json
38
+ {
39
+ "cells": {
40
+ "status": {
41
+ "mat": {
42
+ "imports": [{"name": "OrderBadge", "from": "./order-badge"}],
43
+ "template": "<span class=\\"status\\">{{ row.status }}</span>",
44
+ "styles": ".status { font-weight: 500; }",
45
+ "members": "get label() { return this.row.status.toUpperCase(); }"
46
+ },
47
+ "bs": {"template": "..."},
48
+ "nv": {"template": "..."}
49
+ }
50
+ },
51
+ "filterRules": {"overdue": "row.total > value"},
52
+ "callbacks": {"mat": {"components.sortHeader.afterRender": "console.log(arguments)"}}
53
+ }
54
+ \`\`\`
55
+
56
+ - \`cells\`: by the name of a cell of the spec, then by the skin. \`template\`, \`styles\` and \`members\` are the parts of the component for Angular; \`render\` is the body of \`render()\` for Vanilla JS.
57
+ - \`imports\`: only classes of your project, as \`{name, from}\`. The classes of Angular Material and the pipes of Angular come from the template itself, so leave them out. A string is only the name of such a class (\`"DatePipe"\`), not an import statement.
58
+ - \`filterRules\`: by the name of a filter with \`match: "custom"\`, the body of \`(row, value) => boolean\`.
59
+ - \`callbacks\`: by the skin, then the path of the function in the config of the skin (get_docs, topic skin-config), the body of the function.
60
+ - \`generate_code\` checks the imports before it writes: a wrong entry comes back as an error with its path.`
61
+ },
62
+ {
63
+ name: 'workflow',
64
+ title: 'The order of the tools',
65
+ summary: 'Which tools to call for a new table, a cell, a question or a table that does not render',
66
+ text: () => `
67
+ | Task | Calls |
68
+ | --- | --- |
69
+ | A new table | \`get_started\`, \`inspect_project\`, \`validate_spec\` and \`generate_code\` with a spec, with row details by default (topic spec; \`get_docs\` topic fillings has the format of the fillings); write the templates of the cells, write the files, build the project |
70
+ | A cell, row details or filters for an existing table | \`inspect_project\`, \`scaffold_component\`; write the files |
71
+ | A change of a table, a question about the API | \`get_docs\` with a topic |
72
+ | A table that does not render | \`inspect_project\`, then \`get_docs\` with the topic errors |
73
+
74
+ \`validate_spec\` checks a spec and tells for every problem the path, what is wrong and how it should be; \`generate_code\` runs it first. The server writes no files: the agent writes them.`
75
+ },
76
+ {
77
+ name: 'spec',
78
+ title: 'The table spec, BroTableSpec',
79
+ summary: 'The fields of the spec that validate_spec checks and generate_code takes',
80
+ text: () => `
81
+ The spec is the table as data, with the names of BroCol and BroConfig, without functions and without the tags of a skin.
82
+
83
+ ${schemaLines(specSchema, '').join('\n')}
84
+
85
+ - \`component.cell\` names a custom cell of \`cells\`: the generator writes its component with the tag \`bro-cell-<row-type>-<cell>\`. \`component.element\` is a ready element of the skins.
86
+ - A cell of the kind \`details\` is the row details, named in \`config.rowDetailsComponent\`. Every table has row details by default: a details cell with what the row has no room for, and a column with the element \`details-button\`. A table goes without them only when the request asks for that.
87
+ - A filter compares the field of the row by its control: \`text\` a string with \`contains\` or \`startsWith\`, \`select\` the \`values\` of the field with \`eq\`, \`range\` a number with two keys \`<name>From\` and \`<name>To\`, \`checkbox\` a boolean; \`match: "custom"\` with a \`rule\` in words is a condition that a model writes.
88
+ - \`skinConfig\` holds the settings of one skin with the names of BroConfig: see the topic skin-config.
89
+ - \`cssVars\` sets the CSS variables of the table: see the topic css-variables.
90
+
91
+ Example:
92
+
93
+ \`\`\`json
94
+ ${JSON.stringify(builtinSpecs[0].spec, null, 2)}
95
+ \`\`\``
96
+ },
97
+ {
98
+ name: 'setup',
99
+ title: 'The setup of a project',
100
+ summary: 'The packages, the CSS and the registration of the skin, once for all tables of a project',
101
+ text: () => pairs.map(([target, skin]) => `**${target === 'angular' ? 'Angular' : 'Vanilla JS'}, skin ${skin}**\n\n${setupText(setupSteps(target, skin))}`).join('\n\n')
102
+ },
103
+ {
104
+ name: 'columns',
105
+ title: 'Columns, BroCol',
106
+ summary: 'title, name, dataType, sortKey, tooltip, component, html, className and short titles',
107
+ text: () => `
108
+ | Field | Meaning |
109
+ | --- | --- |
110
+ | \`title\` | The title of the column; an empty string for a column of buttons |
111
+ | \`name\` | The field of the row the column shows |
112
+ | \`tooltip\` | The tooltip of the header |
113
+ | \`dataType\` | \`string\`, \`number\` or \`boolean\`: a number column has the arrow of the sort before its title |
114
+ | \`sortKey\` | The field the column sorts by |
115
+ | \`component\` | \`{selector, attrs, event}\`: the tag of a Custom Element for the cells; \`attrs\` sets attributes from values or functions \`(col, row) => value\`, always as strings and as they are, so an address from the data of a row, as \`href\`, needs a check of its scheme in the code of the page; \`event\` names the event of the element that the table gives the page as a \`bro-action\` action |
116
+ | \`html\` | The values are HTML: the table inserts them without sanitizing, so only trusted values |
117
+ | \`className\` | A class of the header and body cells: its rules are CSS of the application. The spec has no such field: a column of a number field is aligned right by the generator, with the class \`bro-col_text-right\` of the skin; another class goes into \`cols\` of the generated page by hand |
118
+
119
+ The columns of a page are \`BroCol<Row>[]\` of its row type: TypeScript checks \`name\` and \`sortKey\` against the fields of the row.
120
+
121
+ A date or an amount of money is a string field that holds the text as the cell shows it, as \`"Sep 1, 2026"\` or \`"$15"\`, formatted by \`Intl.DateTimeFormat\` and \`Intl.NumberFormat\` with its currency in the language of the other texts of the page (\`'en-US'\` for a page in English, not the default language of the browser, which would mix two languages on one page), never the raw \`2026-09-01\` or \`15\`: the search of the toolbar looks in the string fields of the columns, so the user finds what he sees on the screen. For the order of the sort add a number field with the time or the amount (\`createdAt\`, \`total\`) and name it in \`sortKey\` of the column; the filters (\`range\`) read that number field too.
122
+
123
+ The table measures the widths of the columns itself. A column may get narrower down to the width of its short title, by default the first word of the title; \`config.shortColumnTitle(col)\` gives other short titles. A column of a component is never narrower than its content.
124
+
125
+ Ready elements of the skins:
126
+
127
+ | Element | mat | bs | nv | Value |
128
+ | --- | --- | --- | --- | --- |
129
+ | tags | \`bro-mat-tags\` | \`bro-bs-tags\` | \`bro-nv-tags\` | an array of strings in \`row[col.name]\` |
130
+ | switch | \`bro-mat-slide-toggle\` | \`bro-switch\` | \`bro-switch\` | a boolean; \`bro-switch\` takes it from the attribute \`checked\`: \`attrs: {checked: (_col, row) => row.done}\`. With the attribute \`name\` and \`event: 'action'\` a change sends the action \`name\` with the column and the row |
131
+ | details button | \`bro-details-button\` | the same | the same | \`attrs: {title: 'Details', name: events.detailsToggleEvent}, event: 'action'\` |
132
+
133
+ The tags and the switch are optional elements of the skins, as the menu of the toolbar in the skin mat: the setup of the project gives \`withTags()\`, \`withSwitch()\` and \`withToolbarMenu()\` to \`provideTableBroMatSkin()\`, and calls \`withTags()\` and \`withSwitch()\` after the import of a vanilla skin. \`withTags({texts: {empty: 'No tags'}})\` gives the text of a cell of tags without items, No data by default (since 1.2.0); \`setTagsTexts({empty})\` of bs and nv and the signal \`empty\` of \`TableBroMatTagsTexts\` of mat change it at any time, and the tags on the page take it at once.`
134
+ },
135
+ {
136
+ name: 'config',
137
+ title: 'Config, BroConfig',
138
+ summary: 'The fields of the config, their defaults and how the config of a skin lies under it',
139
+ text: () => `
140
+ | Field | Meaning |
141
+ | --- | --- |
142
+ | \`skin\` | Required: \`mat\`, \`bs\` or \`nv\`, registered by the package of the skin |
143
+ | \`toolbar\` | \`false\` renders the table without its toolbar, as a table in the row details of another table |
144
+ | \`header\` | \`false\` hides the header of the table, as a table in the row details of another table with the same columns |
145
+ | \`toolbarTitle\` | The title of the toolbar |
146
+ | \`pageSize\` | The rows on a page, 5 by default |
147
+ | \`pageSizeOptions\` | The page sizes of the paginator, \`[5, 10, 15, 20, 25, 50, 100]\` by default: it offers the ones up to the number of all rows, the first one above it and the current one |
148
+ | \`searchDebounce\` | The delay of the search after the last key in ms: 100 by default, 400 in the server mode |
149
+ | \`server\` | The rows come from the server by the query of the table: see the topic server-mode |
150
+ | \`rowCounter\` | The column of the row numbers, on by default |
151
+ | \`shadowsEnabled\` | The shadows of the table, on by default |
152
+ | \`theme\` | \`auto\` by default, the theme of the system; \`light\` or \`dark\` for a page that keeps one theme |
153
+ | \`texts\` | The words of the table in the language of the application, English by default (since 1.2.0): first the texts of the core, the parts that the core renders, flat and named as the options of the config: \`rowCounterTitle\`, the title of the column of the row numbers, \`rowCounterLabel\`, its name for screen readers, and \`rowDetailsLoadingMessage\`, what screen readers hear while row details load from the server; then the texts of the components of the skin, by groups in the order of the skin config: \`filterDrawer\` with \`title\` and \`close\`; \`paginator\` with the labels of the paginator of bs and nv in the order of \`MatPaginatorIntl\`, \`itemsPerPageLabel\`, \`nextPageLabel\`, \`previousPageLabel\`, \`firstPageLabel\`, \`lastPageLabel\` and \`getRangeLabel(page, pageSize, length)\`, then \`label\`, the name of the paginator, \`navigationLabel\`, the name of its navigation, and \`firstText\`, \`previousText\`, \`nextText\` and \`lastText\`, the words of its buttons; \`progressBar\` with \`label\`, the name of the progress bar of bs and nv; \`state\` with the messages of the states by their names; \`toolbarMain\` with \`search\` and the visually hidden words after the titles of the toolbar links of bs and nv: \`searchRole(title)\` by the title of the toolbar (Search for Orders, or Search for rows), \`buttonRole\` (Filters button, for search engines only: a screen reader hears the role button), \`menuRole\` (More menu) and \`menuItemRole\` of the items of the menu of bs (Print menu item); \`toolbarSearch\` with \`field\`, \`back\` and \`clear\`; as \`{paginator: {itemsPerPageLabel: 'Rows:'}, state: {'no-data': 'No orders'}}\`. The text of a cell of tags without items belongs to the tags of the skin: \`withTags({texts: {empty}})\`. The flat fields of 1.1.0, as \`itemsPerPage\`, still work and are deprecated; they are removed in 2.0. A field in a group and a text of the core win over their flat field. The paginator of mat takes \`MatPaginatorIntl\` |
154
+ | \`rowDetailsComponent\` | The tag of the element of the row details: see the topic row-details |
155
+ | \`rowDetailsServer\` | The row details load from the server |
156
+ | \`rowDetailsMotion\` | \`'kinetic'\` by default: the row details move as the pages do; \`'eased'\` opens and closes them in \`--bro-details-duration\` |
157
+ | \`shortColumnTitle\` | \`(col) => string\`: the short title of a column |
158
+ | \`filtersTransform\` | \`(rows, filters) => rows\`: the filters of the application in the local mode, see the topic filters |
159
+ | \`components\`, \`css\` | The settings of the skin: see the topic skin-config |
160
+ | \`autoColumnWidth\` | \`false\` turns the auto widths off: the CSS of the application sizes every column by its class |
161
+ | \`styleNonce\` | The nonce of a Content Security Policy for the styles of the column widths |
162
+
163
+ The table takes a copy of the config of the skin, adds the defaults, and puts the config of the application over it: nested objects are merged, arrays and functions are replaced as a whole.`
164
+ },
165
+ {
166
+ name: 'events',
167
+ title: 'Events and actions',
168
+ summary: 'The event bro-action, its actions, and the event bro-error',
169
+ text: () => `
170
+ Every action comes in the event \`bro-action\` on the table, with \`detail = {action, selector, data}\` of the type \`BroActionDetail<Row>\`. The Angular wrapper gives it as the output \`action\`, and the action \`bro-filters-server-loaded\` as the output \`query\`. A check of an action of a custom cell, as \`case 'edit'\` of a \`switch\` on \`action\`, gives \`data\` as \`BroCellActionData<Row>\` (\`data?.row\`); \`BroActionDataMap\` gives the data of each action of the table. In Vanilla JS a listener takes the row type and the names of the custom actions, \`table.addEventListener<Row, 'edit' | 'delete'>('bro-action', event => ...)\` (\`never\` without them), on the table and on the \`table-bro-filter-drawer-container\` that the actions of its table bubble up to: \`data?.row\` is a \`Row\`, and a case of an action of the table narrows \`data\` to its type, as \`BroQuery<Row>\` of \`bro-filters-server-loaded\`, without a cast.
171
+
172
+ | Action | From | data |
173
+ | --- | --- | --- |
174
+ | a name of an action of a custom cell, as \`edit\` | the cell, through \`component.event\` | \`{col, row}\` |
175
+ | the \`eventType\` of a toolbar button, the \`value\` of a menu item | the toolbar | |
176
+ | \`bro-filters-toggle\` | the Filters button of the toolbar | |
177
+ | \`bro-filters-server-loaded\` | the table in the server mode | \`BroQuery\`: \`page\`, \`pageSize\`, \`filter\` with the search, \`sort\` |
178
+ | \`bro-details-toggle\` | the details button or a custom cell: the table opens or closes the row details | |
179
+
180
+ The page sends actions to the table with \`dispatchAction(table, action, data)\`: \`events.filtersFilterEvent\` (\`bro-filters-filter\`) with the filters of the page, \`events.columnsWidthEvent\` with a known new width of the table.
181
+
182
+ The table reports its errors with the DOM event \`bro-error\` on the host, \`detail = {code, message}\`: \`render-failed\` when a write of the table throws. The table shows the state fail.`
183
+ },
184
+ {
185
+ name: 'server-mode',
186
+ title: 'The server mode',
187
+ summary: 'config.server, BroQuery and the rules of delivering the rows',
188
+ text: () => `
189
+ With \`config.server: true\` the table does not sort, filter or page its rows itself. On every gesture it sends the action \`bro-filters-server-loaded\` with a \`BroQuery\`: \`page\`, \`pageSize\`, \`filter\` (the search and the filters of the page) and \`sort\` (\`keys\` with a direction and their \`order\`).
190
+
191
+ Delivery: in Angular the inputs \`rows\` and \`total\` for the first page of a query, or \`rowsToAppend\` and \`total\` for its next pages; in Vanilla JS \`table.replaceRows(rows, {total})\` for the first page of a query or \`table.appendRows(rows, {total})\` for its next pages. A query with other filters or another sort starts from its first page. The table takes the page and the page size of its last query.
192
+
193
+ The table does not ask for its first page: the page loads it itself, \`{page: 1, pageSize}\` with the \`pageSize\` of the config, and the table asks for the pages after it and for the first page of other filters. \`generate_code\` writes it so in both targets: \`initTable()\` of the Angular page, and \`onQueryTable\` with \`loadPage\` in the Vanilla JS module.
194
+
195
+ 1. One batch per delivery: the rows with their total in one call.
196
+ 2. One gesture, one query: only the action of the table starts a load.
197
+ 3. The page drops stale answers: it keeps the query of each load and cancels the old ones (\`switchMap\`, \`AbortController\` or a generation number).
198
+ 4. The filters of the table and the filters of the page are merged: each field has one owner, the page wins.
199
+ 5. A change of a filter of the page loads page 1 and replaces the rows.
200
+ 6. The rows are an array, \`[]\` an empty result; an empty result of a query with a search or filters is the state \`not-found\`, the input \`emptyState\` in Angular, \`replaceRows(rows, {total, emptyState: 'not-found'})\` in Vanilla JS, else the state \`no-data\` of an empty table shows. The state messages go into \`config.texts.state\`, the icons stay the ones of the skin. The loading and a failure are the inputs \`loading\` and \`failed\` in Angular, \`table.setState('loading')\` and \`table.setState('fail')\` in Vanilla JS.
201
+
202
+ In Angular the contracts of \`@table-bro/angular\` hold the parts apart:
203
+ - the page implements \`BroServerContainer<Row>\`: the signals \`cols\`, \`rows\`, \`conf\`, \`total\` and \`rowsToAppend\`, \`initTable()\` that starts the table and asks for page 1, and \`onQueryTable(query)\` that asks the service for a page and delivers it: the first page of a query to \`rows\`, the next pages to \`rowsToAppend\`, an answer to an older query left out;
204
+ - a service implements \`BroService<Row, ApiQuery, ApiPage>\` and sends the requests, as Angular services do: \`loadPage(query)\` requests a page of the API and gives a \`BroServerPage<Row>\`, the rows with their total; \`mapServerQuery(query)\` turns a \`BroQuery\` into the request of the API, \`mapServerResponse(response)\` turns its answer into the rows, field by field from the names of the API;
205
+ - the page holds no request and no map: they live in the service.`
206
+ },
207
+ {
208
+ name: 'row-details',
209
+ title: 'Row details',
210
+ summary: 'The element of the row details, its button and the details that load from the server',
211
+ text: () => `
212
+ \`config.rowDetailsComponent\` names the tag of a Custom Element. The table creates it under a row that opens and sets its property \`row\`.
213
+
214
+ - The details open by the action \`bro-details-toggle\`: the details button (\`bro-details-button\`, see the topic columns) sends it, a custom cell may send it too.
215
+ - With \`config.rowDetailsServer: true\` the element gets the attribute \`server\` and reports its loading with the event \`bro-details-server-loaded\` (\`events.detailsServerLoadedEvent\` of @table-bro/core), \`detail\` the loaded part, 1 at the end. A failed loading is the event \`bro-details-server-failed\` (\`events.detailsServerFailedEvent\`): the row ends its loading closed, and its next opening loads the details again.
216
+ - The element keeps its markup when the table moves it in the DOM: build it once.
217
+
218
+ The scaffold of a details element is the scaffold of a cell with the kind details: see the topic cells.`
219
+ },
220
+ {
221
+ name: 'filters',
222
+ title: 'Filters and the Filter Drawer',
223
+ summary: 'The Filter Drawer of each skin, how the page gives the filters to the table, filtersTransform and the server mode',
224
+ text: () => `
225
+ The filters of the application live outside of the table, in the Filter Drawer next to it. The Filters button of the toolbar (\`events.filtersToggleEvent\`) opens it, and \`toggle()\` of the container too. When the table would get narrower than its minimum width next to the drawer, the drawer lies over the table with a scrim, as the modal side sheet of Material 3; \`mode="over"\` on the container lays it over the table always.
226
+
227
+ | Skin | Container | The filters |
228
+ | --- | --- | --- |
229
+ | mat | \`<table-bro-filter-drawer-container-ng>\` of table-bro.angular-material: it takes the inputs and outputs of \`<table-bro-ng>\` and renders the table | its content |
230
+ | bs, nv | \`<table-bro-filter-drawer-container skin="bs">\` of the core, defined by \`defineTableBroFilterDrawerContainer()\` | an element with \`slot="filters"\` next to the table |
231
+
232
+ The title of the drawer and the name of its close button come from \`texts.filterDrawer\` (\`title\`, \`close\`) of the config of the table in every skin (since 1.2.0); the container of bs and nv reads them when it connects and when it opens, so a new \`configure()\` of the table shows at the next opening. The property \`texts\` of \`<table-bro-filter-drawer-container>\` of 1.1.0 still works and wins over the config of the table; it is deprecated and removed in 2.0.
233
+
234
+ - The page gives the filters to the table: the input \`filter\` of the wrapper, or the action \`bro-filters-filter\` in Vanilla JS. The table accumulates the changes, so a cleared field goes as \`undefined\`.
235
+ - Local mode: the table searches by itself; the other filters go through \`config.filtersTransform(rows, filters)\`. The table calls it only when some filter has a value: undefined, null and '' clear a filter, false and 0 are values. \`matchesFilterCondition(value, {eq, in, contains, startsWith, gte, lte})\` of @table-bro/core compares one value; an undefined condition matches.
236
+ - Server mode: the filters come in \`query.filter\`, and the server applies them.
237
+ - A date filter is the date range picker of Angular Material in mat (\`mat-date-range-input\` with \`provideNativeDateAdapter()\`, the dates go to the filters as \`yyyy-mm-dd\`), an \`<input type="date">\` in bs and nv.
238
+ - The type of the filters is \`BroFilters<Row, {custom filters}>\`; \`search\` and \`advancedFilters\` belong to the table.
239
+
240
+ \`generate_code\` writes the filters component \`app-<row-type>-filters\`, a component of the application as the page \`app-<row-type>-table\`, \`filtersTransform\` and the container from \`filters\` of the spec.`
241
+ },
242
+ {
243
+ name: 'cells',
244
+ title: 'Custom cells: the recipe',
245
+ summary: 'The contract of a cell, the scaffolds for Angular and for Vanilla JS, and the rules',
246
+ text: () => {
247
+ const spec = builtinSpecs[0].spec;
248
+ const cell = spec.cells[2];
249
+ return `
250
+ The table creates the element of \`component.selector\` for every row and for the measure of the widths, sets the properties \`col\`, \`row\` and \`config\` and the class \`bro-cell_component\`, sets the attributes of \`component.attrs\`, and gives the page the event named in \`component.event\` as the action \`detail\` of \`bro-action\`, with \`data = {col, row}\`. A cell implements \`BroCellElement<Row>\` and the row details \`BroRowDetailsElement<Row>\` of the row type, with \`col?: BroCol<Row>\` and \`row?: Row\`: the code reads the fields of the row without a cast.
251
+
252
+ Rules:
253
+
254
+ 1. Names: the tag \`bro-cell-<row-type>-<cell>\`, a prefix that the library leaves to the application, the class \`<RowType><Cell>Cell\` without the prefix \`Bro\` of the library, the file \`<row-type>-<cell>-cell.ts\` next to the page, without the suffix \`.component\`, as \`ng generate\` names the files since Angular 20.
255
+ 2. An Angular component has no \`selector\`: it lives only as a Custom Element. The components of one cell for the three skins with the same template open it with an attribute of their skin, \`<span data-skin="bs" class="status">\`: Angular gives components of the same template one id and one set of styles. Never a static host class, \`host: {class: ...}\`: it replaces the class attribute of the element, with the class \`bro-cell_component\` that the table gives it for the width of its column.
256
+ 3. Register the elements once, before the table: \`customElements.define\` with a \`customElements.get\` guard; Angular needs \`@angular/elements\`.
257
+ 4. The content fits the height of the row. Two lines fit with a smaller font, or with a larger \`--bro-row-height\`: the rows of bs and nv are 40 px, so a cell of two lines with an avatar takes \`--bro-row-height: 52px\` there, and the avatar is 32 px in bs and nv, 40 px in mat.
258
+ 5. The styles of a cell are styles of its component, without rules for the table or other cells.
259
+ 6. No side effects on connect: a request or a subscription per cell runs for every row.
260
+ 7. One output \`action\`, its value is the name of the action.
261
+ 8. Vanilla JS puts values in with \`textContent\`.
262
+ 9. Inputs and outputs with \`@Input()\` and \`@Output()\`, not \`input()\` and \`output()\`; a component imports the classes of Angular Material (\`MatIcon\`), not their NgModules.
263
+ 10. The components of a library keep their own look: no colors, sizes, fonts or tokens (\`--mat-*\`, \`--mdc-*\`, \`--bs-*\`) of their own unless the user asks for them; the styles of a cell only place them. A custom element that needs a color, as an avatar, reads a color role of the theme: \`var(--mat-sys-*)\` for mat, \`var(--bs-*)\` for bs, the system colors (\`Canvas\`, \`CanvasText\`, \`GrayText\`, \`AccentColor\`) for nv. No literal colors.
264
+ 11. Angular Material by the selectors of version 20: \`matButton="text|filled|tonal|outlined|elevated"\`, \`matIconButton\`, \`matMiniFab\`, \`matFab\`; not \`mat-button\`, \`mat-icon-button\` and the other old ones. A tooltip shows above its element: \`matTooltipPosition="above"\`. An icon of a cell or of a dialog is a Material Symbol, the current set of Material icons, Rounded and light (weight 300), with the glyphs of Material 3, as \`badge\`, \`alternate_email\`, \`business_center\`, \`travel_explore\`, \`check_circle\`: \`<mat-icon fontSet="material-symbols-rounded">edit</mat-icon>\`. An icon of a cell or of a dialog of bs is the inline SVG of Bootstrap Icons, the set of Bootstrap 5.3, first: its \`<svg>\` as the site of Bootstrap Icons gives it, of 16 by 16 px, with \`fill="currentColor"\` and \`aria-hidden="true"\`, the counterpart of the Material Symbol of mat, as \`person-vcard\`, \`at\`, \`briefcase\`, \`globe-americas\`, \`check-circle\`. Its paths are never written from memory: an icon of these docs, as the states of the table and the buttons of the skin, takes the SVG they give, and any other icon takes the file \`node_modules/bootstrap-icons/icons/<name>.svg\` of the package \`bootstrap-icons\` or the SVG of https://icons.getbootstrap.com; the page needs no font of Bootstrap Icons and no \`<i class="bi">\`.
265
+ 12. The actions of a row stay in the row, not in a menu, and look as the Details button of the skin, in its color: mat, \`matIconButton\` with a \`mat-icon\` and a tooltip in the primary color, a delete too, \`:host { --mat-icon-button-icon-color: var(--mat-sys-primary); }\`; bs, buttons with the classes \`btn btn-link\` and no size, as \`btn-sm\`; nv, links as buttons in the color of links, \`<a href="" role="button" (click)="$event.preventDefault(); action.emit('edit')">Edit</a>\` with \`a { color: LinkText; }\`.
266
+ 13. The row details start where the cells of the row do: the paddings of the row are on their content, \`dl { padding: 16px 24px; }\` for mat and \`padding: 12px 16px\` for bs and nv. Their host is \`:host { display: block; }\` without paddings or borders: the table opens the details by the height of the host. Values show as pairs of a label and a value in columns, a \`<dl>\` in a grid; a list of items as the chips of Angular Material, the badges of Bootstrap, or one line; no bulleted list. A label takes the secondary color role: \`var(--mat-sys-on-surface-variant)\`, \`var(--bs-secondary-color)\`, \`GrayText\`.
267
+ 14. A progress bar fills the width of its cell, whatever the width of the column: \`mat-progress-bar\` and the \`.progress\` of Bootstrap do it themselves, a native \`<progress>\` or \`<meter>\` takes \`progress { width: 100%; }\`.
268
+ 15. A table in the row details of another table is a part of its row: \`toolbar: false\`, \`header: false\` when its columns are the ones of the main table, \`shadowsEnabled: false\` and \`rowCounter: false\`, and \`pageSize: Math.min(5, rows.length)\`, so it keeps no empty rows under its paginator. It renders when its rows come, with its first config. Its columns line up with the columns of the main table, also when the Filter Drawer makes the table narrower: the main table keeps its auto widths (\`autoColumnWidth\` by default), which it writes for the keys of its columns and recalculates with the drawer, and the inner table has \`autoColumnWidth: false\` and the same columns, so the same keys take the same widths. The inner table keeps the column of the details button with the button hidden, \`visibility: hidden\`.
269
+ 16. The dialogs of the page, as a form of a row or the confirmation of a delete, follow the skin as the cells do: \`MatDialog\` of Angular Material for mat, the \`Modal\` of Bootstrap for bs, the native \`<dialog>\` with \`showModal()\` for nv. Each looks as its library shows it, not in the fonts of the page: mat, \`mat-form-field appearance="outline"\` with an icon \`matPrefix\` and a \`mat-error\`, a gap of 16 px between the fields for the error, text buttons for Cancel and the action, as the dialogs of Material 3 have them, and the confirmation of a delete as the basic dialog of Material 3: a headline, its text and two text buttons, Delete in the color of errors; bs, the markup of the modal of the docs of Bootstrap 5.3: \`h1.modal-title\` with \`font-family: var(--bs-body-font-family)\` and \`font-size: 1.25rem\` in the styles of the dialog, as the page may give its headings and \`.fs-5\` its own font and sizes, a \`.btn-close\` in the header, a \`label.col-form-label\` above each field, and the field in an \`.input-group\` with the SVG of Bootstrap Icons that matches the Material Symbol of the mat dialog in a \`<span class="input-group-text">\`, the buttons \`btn-secondary\` for Cancel and \`btn-primary\` for the action, \`btn-danger\` for Delete; nv, the controls of the browser in \`font-family: system-ui\`. The fields of the dialogs of all skins have one layout, the one of the mat dialog: a grid of two columns with a gap of 16 px, where a long field, as a name, an email or a status, takes the whole line and two short ones share a line, \`.row.g-3\` with \`.col-12\` and \`.col-6\` in bs. The Bootstrap modal keeps the paddings of the docs of Bootstrap, 1rem, also in a project with its own spacer of Bootstrap: \`--bs-modal-padding: 1rem\` and \`--bs-modal-header-padding: 1rem 1rem\` on \`.modal\`, \`--bs-gutter-x: 1rem\` and \`--bs-gutter-y: 1rem\` on the \`.row\` of the fields in place of \`.g-3\`. The buttons of a dialog have no icons.
270
+ 17. A status, a state or a yes or no value is a component, never the raw text or \`true\`: in mat a \`mat-chip\` of Angular Material with a Material Symbol of the state, \`<mat-chip><mat-icon matChipAvatar fontSet="material-symbols-rounded">check_circle</mat-icon>Active</mat-chip>\`, in the color roles of the theme by the tokens of the chip (\`--mat-chip-elevated-container-color: var(--mat-sys-primary-container)\` for a good state, \`var(--mat-sys-error-container)\` for a bad one); a badge of the skin with a dot in bs and nv, the subtle colors of bs (\`badge rounded-pill bg-success-subtle text-success-emphasis border border-success-subtle\`), the system colors of nv (\`AccentColor\`, \`GrayText\`). A yes or no value shows its meaning in words, as Remote or On-site.
271
+
272
+ In an Angular project a cell is an Angular component in every skin, with the markup of the skin in its template: Angular Material for mat, the classes of Bootstrap for bs, plain HTML for nv. Its imports come from the template.
273
+
274
+ \`\`\`ts
275
+ ${cellCode(spec, cell, 'angular', builtinSpecs[0].fillings.cells?.['actions']?.mat).content}\`\`\`
276
+
277
+ On a page without a framework a cell is a Custom Element in Vanilla JS:
278
+
279
+ \`\`\`ts
280
+ ${cellCode(spec, cell, 'vanilla', builtinSpecs[0].fillings.cells?.['actions']?.bs).content}\`\`\``;
281
+ }
282
+ },
283
+ {
284
+ name: 'toolbar',
285
+ title: 'The toolbar',
286
+ summary: 'The buttons and the menu of the toolbar and their actions',
287
+ text: () => `
288
+ \`config.components.toolbarMain.buttons\` holds the buttons and one menu of the toolbar. The toolbar of every skin has only its search; a table with filters needs the Filters button, which opens the Filter Drawer.
289
+
290
+ - A button, \`BroButton\`: \`{buttonTitle, eventType, iconHTML, buttonTooltip}\`. A click sends the action \`eventType\`. In the mat skin a button is an icon with a tooltip: \`iconHTML\` is the name of a Material icon.
291
+ - The Filters button: \`eventType: events.filtersToggleEvent\`, in the mat skin with \`iconHTML: 'filter_list'\`.
292
+ - A menu, \`BroDropdown\`: \`{buttonTitle, mode: 'navbar-dropdown', options: [{value, title, iconHTML}]}\`; an item sends its \`value\`.
293
+ - The titles of the buttons, the menu and its items say what they do, as \`More actions\` or \`Export orders\`, not \`More\` or \`Read more\`: the skin bs renders them as links, and nv its buttons, which screen readers and search engines read by their text. A link that acts on the table, the search, a button or the menu, has the role \`button\`, as Bootstrap gives it to a link that goes nowhere, and takes Space; the items of the menu of bs stay links, as in the markup of Bootstrap. The skin adds visually hidden words after the title that say what the link is: \`texts.toolbarMain.searchRole(title)\` of the search by \`toolbarTitle\` (Search for Orders, or Search for rows without a title), \`texts.toolbarMain.buttonRole\` (\`button\`, which only the text of the link keeps: a screen reader hears its role), \`texts.toolbarMain.menuRole\` of the menu (\`menu\`) and \`texts.toolbarMain.menuItemRole\` of its items (\`menu item\`); an empty string turns them off.
294
+
295
+ The search is not among these buttons: the toolbar of every table has its search button first, in every skin, and there is nothing to turn on. A click opens the field of the search. In the local mode the table searches the string fields of its columns, so a date or an amount that the cell shows as a string is found by the text the user sees (get_docs, topic columns). With \`config.server: true\` the text goes to the server as \`filter.search\` of the query. \`components.toolbarSearch.beforeRender\` of the config of the skin changes the opened field.`
296
+ },
297
+ {
298
+ name: 'skin-config',
299
+ title: 'The settings of a skin',
300
+ summary: 'The components and the classes of a skin, its functions, and custom elements in place of its elements',
301
+ text: () => `
302
+ The config of the application changes any value or function of the config of the skin before \`configure\`, without a new build of the skin. The exported configs are \`matSkinConfig\` (@table-bro/angular-material), \`bootstrapSkinConfig\` (@table-bro/bootstrap) and \`nativeSkinConfig\` (@table-bro/native).
303
+
304
+ \`themeAttribute\` of the config of a skin is the attribute of the color mode of its UI library, which the table sets on its host to \`light\` or \`dark\` by its theme: \`data-bs-theme\` in the skin bs; the skins mat and nv have none.
305
+
306
+ The values:
307
+
308
+ ${shapeLines(skinConfigShape.css, 'css').concat(shapeLines(skinConfigShape.components, 'components')).join('\n')}
309
+
310
+ The functions:
311
+
312
+ | Path | Signature | Skins | When |
313
+ | --- | --- | --- | --- |
314
+ ${skinCallbacks.map(callback => `| \`${callback.path}\` | \`${callback.signature}\` | ${callback.skins.join(', ')} | ${callback.description} |`).join('\n')}
315
+
316
+ The elements of the mat skin are Angular components with their own templates: they read no classes of the config and call no functions themselves; the functions that the core calls work in every skin, as the column Skins above says. A function replaces the function of the skin: keep what the skin does there, see its config.
317
+
318
+ A custom element in place of an element of a skin follows its contract, a type of Table, Bro: ${Object.entries(skinElementContracts).map(([name, contract]) => `${name} \`${contract}\``).join(', ')}.
319
+
320
+ A config gives only what it changes, at any depth: \`components\` of BroConfig is a \`BroDeepPartial<BroSkinComponents>\`.`
321
+ },
322
+ {
323
+ name: 'css-variables',
324
+ title: 'The CSS variables',
325
+ summary: 'The sizes and timings of a table without a new build of the skin',
326
+ text: () => `
327
+ A skin declares its sizes and timings as CSS variables on the table, and the table reads them at render. A value set on the table changes the CSS and the script together, without a new build of the skin. Set it on the element itself, before the table renders: a global rule by the id of the table (\`#orders-table { --bro-row-height: 64px; }\`, the input \`tableId\` of the wrapper), or \`table.style.setProperty\` before \`configure\`. The container of the Filter Drawer reads its own variables.
328
+
329
+ | Variable | Value | Element | Meaning |
330
+ | --- | --- | --- | --- |
331
+ ${cssVariables.map(variable => `| \`${variable.name}\` | ${variable.kind} | ${variable.target === 'both' ? 'table and drawer container' : variable.target === 'drawer' ? 'drawer container' : 'table'} | ${variable.description} |`).join('\n')}
332
+
333
+ The table and the Filter Drawer have the rounded corners of Material 3 by default, 12 px and 16 px. For square corners set \`--bro-corner-radius\` and \`--bro-filter-drawer-corner-radius\` to \`0\`.
334
+
335
+ Not settable: ${Object.entries(excludedCssVariables).map(([name, reason]) => `\`${name}\`: ${reason}`).join('; ')}.
336
+
337
+ Other sizes, fonts and colors of a skin need changes of its SCSS and a new build.`
338
+ },
339
+ {
340
+ name: 'errors',
341
+ title: 'A table that does not render',
342
+ summary: 'The event bro-error and the usual causes',
343
+ text: () => `
344
+ - \`configure\` throws: \`config.skin\` is missing, or its skin is not registered (import the skin package, or two copies of @table-bro/core on the page).
345
+ - No layout, no styles: the CSS of the core or of the skin is missing.
346
+ - Empty custom cells: the tag is not defined before the table, or the tag in \`component.selector\` differs from the defined one.
347
+ - An Angular cell shows a raw \`<mat-icon>\`: the component imports an NgModule where it needs the class (\`MatIcon\`), or does not import it.
348
+ - The state fail with \`bro-error\` \`render-failed\`: a write of the table threw, the message tells where.
349
+ - The rows of the server mode do not come: the page does not answer \`bro-filters-server-loaded\`, see the topic server-mode.`
350
+ },
351
+ {
352
+ name: 'angular-wrapper',
353
+ title: 'The Angular wrapper, <table-bro-ng>',
354
+ summary: 'The inputs and outputs of the wrapper of @table-bro/angular',
355
+ text: () => `
356
+ | Input | Meaning |
357
+ | --- | --- |
358
+ | \`cols\`, \`conf\` | The columns and the config; a new value creates a new table |
359
+ | \`rows\` | An array replaces the rows, \`[]\` an empty state; without a value the table keeps what it shows |
360
+ | \`loading\` | \`true\`: the loading state, or a refresh over the shown rows |
361
+ | \`failed\` | \`true\`: the failure state |
362
+ | \`total\` | The total of the server, read together with \`rows\` or \`rowsToAppend\` |
363
+ | \`rowsToAppend\` | The rows of the next page for the last \`query\` |
364
+ | \`filter\` | The filters of the page, as of a Filter Drawer |
365
+ | \`emptyState\` | \`not-found\` for an empty answer to a query with filters |
366
+ | \`tableId\` | The \`id\` of \`<table-bro>\` |
367
+ | \`theme\` | \`light\`, \`dark\` or \`auto\` |
368
+
369
+ | Output | Meaning |
370
+ | --- | --- |
371
+ | \`query\` | The \`BroQuery\` of the server mode |
372
+ | \`action\` | Every other action of \`bro-action\`: \`BroActionDetail<Row>\`, with \`data?.row\` for custom cells |
373
+
374
+ The wrapper creates the table outside of the Angular zone; its outputs emit inside it.`
375
+ },
376
+ {
377
+ name: 'ssr',
378
+ title: 'Server-side rendering, the prerender',
379
+ summary: 'The prerender of the loading state on a server: <table-bro-ng> in Angular SSR, prerenderTableBro() without Angular, and its limits',
380
+ text: () => `
381
+ - The prerender renders a table in its loading state on a server: the toolbar, the column headers, the loading row and the paginator, in the markup of the skin, sized by its CSS. The page shows the table before any script runs; in the browser the table renders with its rows in the same element, and the loading animation goes on.
382
+ - Angular: \`<table-bro-ng>\` prerenders during the server-side rendering of Angular itself, in the skins mat, bs and nv; the template and the inputs stay as in the browser. The application needs the server render of Angular: \`@angular/ssr\` with \`provideServerRendering()\` in the config of the server, and \`provideClientHydration()\`. The skin mat renders on the server by \`provideTableBroMatSkin()\`. The skins bs and nv render there when a file of the server bundle imports \`@table-bro/bootstrap\` or \`@table-bro/native\`, as \`app.config.ts\` or the component of the table; a skin that only \`main.ts\` imports leaves \`<table-bro-ng>\` empty on the server.
383
+ - Without Angular: \`prerenderTableBro(element, {cols, conf})\` of \`@table-bro/core\` renders into a \`<table-bro>\` of a server DOM, appended to its document before the call. The rows wait for the browser, where \`configure({cols, rows, conf})\` on the same element renders the table in its place. The imports of the core and of the skins bs and nv work without a DOM: they register the skins and define no elements.
384
+ - Limits: the loading state only. With the theme \`auto\` the prerendered table takes no theme class and shows the theme of the page, since the server knows no system theme. The elements of a skin render on the server only where the skin has \`BroSkinConfig.defineElements(registry)\` (bs, nv) and the window of the server DOM has \`customElements\`; else they stay empty tags until the browser renders the table. Angular inlines the critical CSS of a prerendered page with Beasties, and the CSS of the core and of the skins keeps the layout of a prerendered table there.
385
+ - The page of Docs with the code: get_docs topic \`ssr/prerendering\`.`
386
+ },
387
+ // The pages of Docs on table-bro.com, from the same source as the site
388
+ ...docPages.map(page => ({ name: page.path, title: page.name, summary: `The page of Docs ${page.name}, with its code`, text: () => docPageMarkdown(page) }))
389
+ ];
390
+ /** The lines of a JSON Schema: the path, the type and the description of every field. */
391
+ function schemaLines(schema, path) {
392
+ const lines = [];
393
+ const properties = schema['properties'] ?? schema['items']?.['properties'] ?? {};
394
+ const required = schema['required'] ?? schema['items']?.['required'] ?? [];
395
+ for (const [name, property] of Object.entries(properties)) {
396
+ const fieldPath = path ? `${path}.${name}` : name;
397
+ const type = ('const' in property) ? JSON.stringify(property['const']) : property['enum'] ? property['enum'].map((value) => JSON.stringify(value)).join(' | ') : (property['type'] === 'array' ? `${property['items']?.['type'] ?? 'object'}[]` : property['type'] ?? 'object');
398
+ lines.push(`- \`${fieldPath}\`${required.includes(name) ? '' : '?'}: ${type}${property['description'] ? `: ${property['description']}` : ''}`);
399
+ if ((property['type'] === 'object' && property['properties'] && path.split('.').length < 2 && name !== 'cssVars' && name !== 'skinConfig') || (property['type'] === 'array' && property['items']?.['properties'])) {
400
+ lines.push(...schemaLines(property, property['type'] === 'array' ? `${fieldPath}[]` : fieldPath).map(line => ' ' + line));
401
+ }
402
+ }
403
+ return lines;
404
+ }
405
+ function shapeLines(shape, path) {
406
+ if (typeof shape === 'string') {
407
+ return [`- \`${path}\`: ${shape === 'state' ? '[icon, message]' : shape}`];
408
+ }
409
+ return Object.entries(shape).flatMap(([name, item]) => shapeLines(item, `${path}.${name}`));
410
+ }
@@ -0,0 +1,23 @@
1
+ import type { BroDocTopic } from './docs/topics.js';
2
+ import type { BroSetupStep } from './generate/setup.js';
3
+ import type { BroJsonSchema } from './spec/schema.js';
4
+ import type { BroSpecIssues } from './spec/validate-spec.js';
5
+ /**
6
+ * The part of the MCP that a paid module of the scope @table-bro-extended brings: the default export of the file named by the field
7
+ * table-bro.mcp of its manifest. Without the module the free MCP does not know its fields.
8
+ */
9
+ export interface BroMcpExtension {
10
+ /** The npm package of the module, as @table-bro-extended/island-details. */
11
+ package: string;
12
+ /**
13
+ * The fields of the config of the spec that the module adds, each with its check, which also sees the whole config;
14
+ * the generator writes them as they are.
15
+ */
16
+ config?: Record<string, (value: unknown, path: string, issues: BroSpecIssues, config: Record<string, unknown>) => void>;
17
+ /** The JSON Schema of these fields for the strict answer of the model on the site, as {"rowDetailsIsland": {...}}. */
18
+ configSchema?: Record<string, BroJsonSchema>;
19
+ /** The topics of the reference. */
20
+ topics?: BroDocTopic[];
21
+ /** The setup of a project for the module, added when a spec uses one of its fields. */
22
+ setup?: BroSetupStep[];
23
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import type { BroMcpExtension } from '../extension.js';
2
+ import type { BroSpecSkin, BroTableSpec } from '../spec/spec.js';
3
+ import { type BroCellCode } from './cells.js';
4
+ import type { BroSpecFillings } from './fillings.js';
5
+ export interface BroGeneratedFile {
6
+ /** The path from the folder where the table goes, as order-table/order.ts. */
7
+ path: string;
8
+ content: string;
9
+ /** What the file holds, in a line: the code view and the agent show it before the file. */
10
+ description?: string;
11
+ }
12
+ export interface BroTableFiles {
13
+ files: BroGeneratedFile[];
14
+ todo: string[];
15
+ }
16
+ /**
17
+ * The table for an Angular project: the type of the row, the page component with <table-bro-ng> and its cols, rows and
18
+ * config, the cells, the filters. The files go in the order a reader needs them.
19
+ */
20
+ export declare function angularTable(spec: BroTableSpec, skin: BroSpecSkin, fillings: BroSpecFillings, tagSuffix?: string, extensions?: BroMcpExtension[], simulateServer?: boolean): BroTableFiles;
21
+ /** The columns of the API: an own cell by its tag, a ready element by the tag of the skin. */
22
+ export declare function colsValue(spec: BroTableSpec, skin: BroSpecSkin, cells: BroCellCode[]): unknown[];
23
+ /** The line about the model: the type of a row, and of the filters of the drawer. */
24
+ export declare function modelDescription(hasFilters: boolean): string;
25
+ /** The line about a cell: what it shows, or the row details. */
26
+ export declare function cellDescription(cell: BroCellCode): string;