@adia-ai/web-components 0.7.27 → 0.7.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,22 @@
1
1
  # Changelog — @adia-ai/web-components
2
+ ## [0.7.28] — 2026-07-14
3
+
4
+ ### Added
5
+ - **`table-ui` (`components/table/cell-types.js`) — `markdown` cell type: inline `` `code` ``/**bold**/*italic*/`[link](href)` rendered as real elements, not a plain string.** TKT-0008 (site-a2ui re-verification, direction 1): docs reference tables (props/API tables) mix prose with multiple inline `<code>` spans per cell — content the `{columns, data}` JSON contract previously couldn't carry, forcing a preserved-HTML fallback that dropped the table from the A2UI graph entirely. Renders via `core/markdown.js`'s `inline()` (now exported) — the exact encode/decode pairing the docs-transpiler's own `convertInline()` already produces elsewhere in the pipeline. `[title]` carries the tag-stripped plain text for the truncation tooltip. New unit suite in `table.test.js` (3 cases).
6
+
7
+ ### Changed
8
+ - **`core/markdown.js` — `inline()` is now exported.** Was module-private; reused by `cell-types.js`'s new `markdown` cell type as the client-side half of the docs-transpiler's `convertInline()` contract. No behavior change to existing callers.
9
+
10
+ ### Fixed — TKT-0010, corpus admission-validation content gaps
11
+ - **`kbd.yaml` — declares a `textContent` prop.** Every other text-bearing leaf (`Text`, `Code`) already declares one; `Kbd` was missing it, so the transpiler's unconditional `textContent` extraction for leaf types produced a value the catalog schema rejected as an unevaluated property on every harvested `<kbd-ui>` chunk (12 instances). Purely a schema declaration gap — no behavior change (the transpiler already extracted the value correctly).
12
+ - **`chart.yaml`/`table.yaml` — `data` prop declares `items: {type: object}`.** Opts these two dynamic array props into `@adia-ai/a2ui-corpus`'s new `DynamicObjectList` schema (array-of-object) instead of the default `DynamicStringList` (array-of-string) — the wrong shape for chart/table row records. Paired with a transpiler fix in `@adia-ai/a2ui-compose` (the attribute value was never actually parsed); see that package's CHANGELOG.
13
+
14
+ ### Fixed — GH #224, SVG zero-viewport silent non-render
15
+ - **`styles/resets.css` — `svg` gets its own reset rule, dropping `max-inline-size`/`block-size` instead of sharing `img`/`picture`/`video`/`canvas`'s `max-inline-size: 100%; block-size: auto` rule.** Per the SVG spec, a zero-size viewport — exactly what `max-inline-size: 100%` computes to inside a zero-intrinsic-size ancestor (the common `width:0; height:0; transform:scale(s)` pattern for a CSS-scaled canvas/scene) — makes the element **not render at all**, not "render at 0 visual size": silent, no console warning. `img`/`picture`/`video`/`canvas` don't share this failure mode. Components that embed SVG (`icon-ui`, etc.) already size it explicitly in their own scoped CSS and are unaffected either way (verified: no component CSS relies on the global rule's sizing behavior). Verified via the full web-modules test suite + a manual repro check against the reporting issue's own reproduction case (no dedicated new test — a global CSS reset rule).
16
+
17
+ ### Maintenance
18
+ - **`dist/` CDN bundles rebuilt** (`host.min.css`, `host.sheet.js`, `theme-provider.min.js`, `web-components.min.{css,js}`, `web-components.sheet.js`) to inline the `resets.css` fix above.
19
+
2
20
  ## [0.7.27] — 2026-07-12
3
21
 
4
22
  Fixes harvested from the /site → A2UI docs migration's live operator reports (TKT-0002…0006) — each was a real framework defect the migration surfaced, not a docs-only issue — plus the router race that three separate flake classes traced back to.
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "data": {
57
57
  "description": "JS property (set programmatically — `el.data = [...]`). An array of plain objects; each object's keys are named by the `x` and `y` attributes — e.g. `<chart-ui x=\"month\" y=\"revenue\">` consumes `[{month:'Jan', revenue:3200}, {month:'Feb', revenue:4100}]`. The Chart.js `{labels, datasets}` envelope is NOT chart-ui's API — passing it (or any non-array value) renders an empty chart. May also be supplied declaratively as a JSON-array `data=\"[…]\"` attribute, hydrated once at connect. Custom accessor on the element class, not a reflected attribute.",
58
- "$ref": "common_types.json#/$defs/DynamicStringList"
58
+ "$ref": "common_types.json#/$defs/DynamicObjectList"
59
59
  },
60
60
  "format": {
61
61
  "description": "Number-format mode applied to axis labels + value overlays + donut total + gauge value + treemap value + funnel value + internal tooltip. `abbr` is the legacy 1.2K / 3M format; `decimal` fixes 2 decimals; `currency` prefixes via `--chart-currency-prefix` token (default \"$\"); `percent` multiplies × 100 and adds a % suffix.",
@@ -122,6 +122,8 @@ props:
122
122
  hydrated once at connect. Custom accessor on the element class, not a
123
123
  reflected attribute.
124
124
  type: array
125
+ items:
126
+ type: object
125
127
  default: []
126
128
  dynamic: true # JS-set collection prop with a custom setter — deliberately not in static properties
127
129
  events:
@@ -25,6 +25,10 @@
25
25
  "lg"
26
26
  ],
27
27
  "default": ""
28
+ },
29
+ "textContent": {
30
+ "description": "Key label text, extracted from innerHTML (e.g. \"Ctrl+K\"). The main payload field for Kbd components extracted from HTML — mirrors Text/Code's textContent prop.",
31
+ "$ref": "common_types.json#/$defs/DynamicString"
28
32
  }
29
33
  },
30
34
  "required": [
@@ -15,4 +15,6 @@ import { UIElement } from '../../core/element.js';
15
15
  export class UIKbd extends UIElement {
16
16
  /** Sizing scale: sm, md (default), lg. */
17
17
  size: 'sm' | 'md' | 'lg';
18
+ /** Key label text, extracted from innerHTML (e.g. "Ctrl+K"). The main payload field for Kbd components extracted from HTML — mirrors Text/Code's textContent prop. */
19
+ textContent: string;
18
20
  }
@@ -19,6 +19,11 @@ props:
19
19
  - md
20
20
  - lg
21
21
  reflect: true
22
+ textContent:
23
+ description: Key label text, extracted from innerHTML (e.g. "Ctrl+K"). The main payload field for Kbd components extracted from HTML — mirrors Text/Code's textContent prop.
24
+ type: string
25
+ default: ''
26
+ dynamic: true
22
27
  events: {}
23
28
  slots:
24
29
  default:
@@ -1,5 +1,7 @@
1
1
  // cell-types.js - Built-in cell type registry for table-ui
2
2
 
3
+ import { inline } from '../../core/markdown.js';
4
+
3
5
  const cellTypes = {};
4
6
 
5
7
  function registerCellType(name, config) {
@@ -233,7 +235,34 @@ registerCellType('link', {
233
235
  sortFn: 'alphanumeric',
234
236
  });
235
237
 
236
- // 11. progress
238
+ // 11. markdown — TKT-0008: cells with inline `code`/**bold**/*italic*/
239
+ // [link](href) content the docs-transpiler couldn't express as a plain
240
+ // string (table.yaml {columns,data} contract carries plain values; this
241
+ // is the one escape hatch for INLINE-safe rich text, reusing the same
242
+ // inline() converter core/markdown.js already round-trips against
243
+ // scripts/build/lib/docs-transpiler.mjs's convertInline() — the two are
244
+ // paired encode/decode halves of one contract, not independent parsers).
245
+ // Block-level content (nested tables, component tags, colspan/rowspan)
246
+ // stays out of scope; those still fall back to the preserved-HTML Code
247
+ // panel synthesizeTable already emits.
248
+ registerCellType('markdown', {
249
+ render(value, row, cell) {
250
+ let s = cell.firstElementChild;
251
+ if (!s || s.tagName !== 'SPAN') {
252
+ cell.replaceChildren();
253
+ s = cell.appendChild(document.createElement('span'));
254
+ }
255
+ s.innerHTML = inline(String(value ?? ''));
256
+ cell.title = s.textContent;
257
+ },
258
+ format(value) {
259
+ return String(value ?? '');
260
+ },
261
+ align: 'left',
262
+ sortFn: 'alphanumeric',
263
+ });
264
+
265
+ // 12. progress
237
266
  registerCellType('progress', {
238
267
  render(value, row, cell) {
239
268
  let wrapper = cell.querySelector('row-ui');
@@ -262,7 +291,7 @@ registerCellType('progress', {
262
291
  sortFn: 'numeric',
263
292
  });
264
293
 
265
- // 12. actions
294
+ // 13. actions
266
295
  registerCellType('actions', {
267
296
  render(value, row, cell, meta) {
268
297
  const actions = meta?.actions || [];
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "data": {
24
24
  "description": "Row records. Array of plain objects keyed to columns[].key.",
25
- "$ref": "common_types.json#/$defs/DynamicStringList"
25
+ "$ref": "common_types.json#/$defs/DynamicObjectList"
26
26
  },
27
27
  "density": {
28
28
  "description": "Controls cell padding and row height. 'compact' for dense data, 'standard' for default spacing, 'comfortable' for spacious rows.",
@@ -389,3 +389,40 @@ describe('table-ui — F6 per-row expand suppression (rowExpandable)', () => {
389
389
  expect(el.expanded).toContain(1);
390
390
  });
391
391
  });
392
+
393
+ describe('table-ui — "markdown" cell type (TKT-0008)', () => {
394
+ it('renders inline `code`/**bold**/*italic*/[link](href) as real elements, not literal syntax', async () => {
395
+ const el = mount('<table-ui></table-ui>');
396
+ el.columns = [{ key: 'desc', label: 'Description', type: 'markdown' }];
397
+ el.data = [{ desc: 'Semantic status: `new` | `updated` | **beta** or *deprecated*, see [docs](/site/components/badge).' }];
398
+ await raf();
399
+
400
+ const cell = el.querySelector('[role="gridcell"][data-key="desc"]');
401
+ expect([...cell.querySelectorAll('code')].map((c) => c.textContent)).toEqual(['new', 'updated']);
402
+ expect(cell.querySelector('strong').textContent).toBe('beta');
403
+ expect(cell.querySelector('em').textContent).toBe('deprecated');
404
+ expect(cell.querySelector('a').getAttribute('href')).toBe('/site/components/badge');
405
+ expect(cell.textContent).not.toMatch(/`|\*\*|\[docs\]/); // no literal markdown syntax leaked through
406
+ });
407
+
408
+ it('sets [title] to the plain-text (tag-stripped) version, for the truncation tooltip', async () => {
409
+ const el = mount('<table-ui></table-ui>');
410
+ el.columns = [{ key: 'prop', label: 'Prop', type: 'markdown' }];
411
+ el.data = [{ prop: '`variant`' }];
412
+ await raf();
413
+
414
+ const cell = el.querySelector('[role="gridcell"][data-key="prop"]');
415
+ expect(cell.title).toBe('variant');
416
+ });
417
+
418
+ it('a plain value with no markdown syntax renders as ordinary text (no stray elements)', async () => {
419
+ const el = mount('<table-ui></table-ui>');
420
+ el.columns = [{ key: 'desc', label: 'Description', type: 'markdown' }];
421
+ el.data = [{ desc: 'Plain sentence, no special characters.' }];
422
+ await raf();
423
+
424
+ const cell = el.querySelector('[role="gridcell"][data-key="desc"]');
425
+ expect(cell.querySelector('code, strong, em, a')).toBeNull();
426
+ expect(cell.textContent).toBe('Plain sentence, no special characters.');
427
+ });
428
+ });
@@ -27,6 +27,8 @@ props:
27
27
  data:
28
28
  description: Row records. Array of plain objects keyed to columns[].key.
29
29
  type: array
30
+ items:
31
+ type: object
30
32
  default: []
31
33
  dynamic: true # JS-set collection prop with a custom setter — deliberately not in static properties
32
34
  density:
package/core/markdown.js CHANGED
@@ -91,7 +91,7 @@ export function renderMarkdown(src) {
91
91
  const CODE_PH = (n) => `\0CODE${n}\0`;
92
92
  const CODE_PH_RE = /\0CODE(\d+)\0/g;
93
93
 
94
- function inline(text) {
94
+ export function inline(text) {
95
95
  const escaped = esc(text);
96
96
 
97
97
  // 1. Extract inline-code spans FIRST and protect their (already-escaped,