@oml/markdown 0.11.0 → 0.13.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 (37) hide show
  1. package/out/md/md-execution.d.ts +16 -0
  2. package/out/md/md-executor.d.ts +1 -0
  3. package/out/md/md-executor.js +219 -35
  4. package/out/md/md-executor.js.map +1 -1
  5. package/out/renderers/diagram-renderer.js +160 -1
  6. package/out/renderers/diagram-renderer.js.map +1 -1
  7. package/out/renderers/graph-renderer.js +452 -18
  8. package/out/renderers/graph-renderer.js.map +1 -1
  9. package/out/renderers/matrix-renderer.d.ts +0 -2
  10. package/out/renderers/matrix-renderer.js +45 -40
  11. package/out/renderers/matrix-renderer.js.map +1 -1
  12. package/out/renderers/renderer.d.ts +4 -1
  13. package/out/renderers/renderer.js +98 -0
  14. package/out/renderers/renderer.js.map +1 -1
  15. package/out/renderers/table-renderer.d.ts +4 -2
  16. package/out/renderers/table-renderer.js +104 -38
  17. package/out/renderers/table-renderer.js.map +1 -1
  18. package/out/renderers/types.d.ts +16 -0
  19. package/out/renderers/wikilink-utils.d.ts +1 -0
  20. package/out/renderers/wikilink-utils.js +60 -32
  21. package/out/renderers/wikilink-utils.js.map +1 -1
  22. package/out/static/browser-runtime.bundle.js +7452 -1297
  23. package/out/static/browser-runtime.bundle.js.map +4 -4
  24. package/out/static/browser-runtime.js +15 -2
  25. package/out/static/browser-runtime.js.map +1 -1
  26. package/package.json +2 -2
  27. package/src/md/md-execution.ts +20 -0
  28. package/src/md/md-executor.ts +268 -40
  29. package/src/renderers/diagram-renderer.ts +167 -1
  30. package/src/renderers/graph-renderer.ts +512 -12
  31. package/src/renderers/matrix-renderer.ts +57 -44
  32. package/src/renderers/renderer.ts +105 -1
  33. package/src/renderers/table-renderer.ts +151 -39
  34. package/src/renderers/types.ts +20 -0
  35. package/src/renderers/wikilink-utils.ts +66 -31
  36. package/src/static/browser-runtime.ts +20 -2
  37. package/src/static/markdown-webview.css +44 -15
@@ -1,8 +1,7 @@
1
1
  // Copyright (c) 2026 Modelware. All rights reserved.
2
2
 
3
3
  import { QueryMarkdownBlockRenderer } from './renderer.js';
4
- import type { MdBlockExecutionResult } from './types.js';
5
- import { appendTokenizedValueParts, createWikiLinkElement, isIriValue, shortLabelFromIri } from './wikilink-utils.js';
4
+ import type { MdBlockExecutionResult, MdTableCell, MdTableCellValue } from './types.js';
6
5
 
7
6
  type MatrixStyleRuleKind = 'row' | 'column' | 'cell' | 'header';
8
7
  type MatrixStyleRuleTarget = 'cell' | 'value';
@@ -44,13 +43,15 @@ type MatrixSelectorContext =
44
43
  | { kind: 'header'; context: MatrixHeaderContext };
45
44
 
46
45
  type MatrixCellRecord = {
47
- values: string[];
46
+ cell: MdTableCell;
48
47
  };
49
48
 
50
49
  type MatrixModel = {
51
50
  rowKeys: string[];
52
51
  columnKeys: string[];
53
52
  cells: Map<string, MatrixCellRecord>;
53
+ rowHeaders: Map<string, MdTableCell>;
54
+ columnHeaders: Map<string, MdTableCell>;
54
55
  };
55
56
 
56
57
  export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
@@ -79,7 +80,7 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
79
80
 
80
81
  const stylesheet = compileMatrixStylesheet(result.options);
81
82
  const rowColumnLabel = extractRowColumnLabel(result.options);
82
- const model = this.buildMatrixModel(payloadRows, rowIndex, columnIndex, valueIndex);
83
+ const model = this.buildMatrixModel(payloadRows, result.payload?.rowsTyped ?? [], rowIndex, columnIndex, valueIndex);
83
84
  container.appendChild(this.renderMatrix(model, stylesheet, rowColumnLabel));
84
85
  return container;
85
86
  }
@@ -167,7 +168,8 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
167
168
  for (const [columnIndex, columnName] of visibleColumns.entries()) {
168
169
  const th = document.createElement('div');
169
170
  th.className = 'table-header-cell';
170
- const label = this.renderHeaderLabel(columnName);
171
+ const label = this.renderCellValue(columnName, model.columnHeaders.get(columnName));
172
+ label.style.fontWeight = '400';
171
173
  th.appendChild(label);
172
174
  this.applyStylesheet(th, undefined, stylesheet, [
173
175
  { kind: 'column', context: { index: columnIndex, name: columnName } }
@@ -183,7 +185,7 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
183
185
 
184
186
  const rowHeaderCell = document.createElement('div');
185
187
  rowHeaderCell.className = 'table-cell';
186
- const rowHeaderValue = this.renderValueParts([rowName]);
188
+ const rowHeaderValue = this.renderCellValue(rowName, model.rowHeaders.get(rowName));
187
189
  rowHeaderCell.appendChild(rowHeaderValue);
188
190
  const rowContext: MatrixRowContext = { index: rowCounter, name: rowName };
189
191
  this.applyStylesheet(rowHeaderCell, rowHeaderValue, stylesheet, [
@@ -195,13 +197,13 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
195
197
  for (const [columnIndex, columnName] of visibleColumns.entries()) {
196
198
  const key = buildMatrixKey(rowName, columnName);
197
199
  const record = model.cells.get(key);
198
- const values = record?.values ?? [];
199
- const displayValue = values.join(' ');
200
+ const recordCell = record?.cell;
201
+ const displayValue = this.formatCellDisplayText(recordCell?.value ?? '', recordCell);
200
202
  const columnContext: MatrixColumnContext = { index: columnIndex, name: columnName };
201
203
 
202
204
  const td = document.createElement('div');
203
205
  td.className = 'table-cell';
204
- const valueElement = this.renderValueParts(values);
206
+ const valueElement = this.renderCellValue(recordCell?.value ?? '', recordCell);
205
207
  td.appendChild(valueElement);
206
208
 
207
209
  const cellContext: MatrixCellContext = {
@@ -241,61 +243,49 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
241
243
  return columns.join(' ');
242
244
  }
243
245
 
244
- private buildMatrixModel(rows: string[][], rowIndex: number, columnIndex: number, valueIndex: number): MatrixModel {
246
+ private buildMatrixModel(
247
+ rows: string[][],
248
+ rowsTyped: MdTableCell[][],
249
+ rowIndex: number,
250
+ columnIndex: number,
251
+ valueIndex: number
252
+ ): MatrixModel {
245
253
  const rowKeys: string[] = [];
246
254
  const columnKeys: string[] = [];
247
255
  const seenRows = new Set<string>();
248
256
  const seenColumns = new Set<string>();
249
257
  const cells = new Map<string, MatrixCellRecord>();
258
+ const rowHeaders = new Map<string, MdTableCell>();
259
+ const columnHeaders = new Map<string, MdTableCell>();
250
260
 
251
- for (const row of rows) {
261
+ for (let index = 0; index < rows.length; index += 1) {
262
+ const row = rows[index];
263
+ const typedRow = rowsTyped[index] ?? [];
252
264
  const rowKey = row[rowIndex] ?? '';
253
265
  const columnKey = row[columnIndex] ?? '';
254
- const value = row[valueIndex] ?? '';
266
+ const rowCell = typedRow[rowIndex] ?? literalCell(rowKey);
267
+ const columnCell = typedRow[columnIndex] ?? literalCell(columnKey);
268
+ const valueCell = typedRow[valueIndex] ?? literalCell(row[valueIndex] ?? '');
255
269
  if (!rowKey || !columnKey) {
256
270
  continue;
257
271
  }
258
272
  if (!seenRows.has(rowKey)) {
259
273
  seenRows.add(rowKey);
260
274
  rowKeys.push(rowKey);
275
+ rowHeaders.set(rowKey, rowCell);
261
276
  }
262
277
  if (!seenColumns.has(columnKey)) {
263
278
  seenColumns.add(columnKey);
264
279
  columnKeys.push(columnKey);
280
+ columnHeaders.set(columnKey, columnCell);
265
281
  }
266
282
  const key = buildMatrixKey(rowKey, columnKey);
267
- const record = cells.get(key) ?? { values: [] };
268
- record.values.push(value);
283
+ const record = cells.get(key) ?? { cell: { ...valueCell, values: valueCell.values.slice() } };
284
+ mergeCellValues(record.cell, valueCell.values);
269
285
  cells.set(key, record);
270
286
  }
271
287
 
272
- return { rowKeys, columnKeys, cells };
273
- }
274
-
275
- private renderValueParts(values: string[]): HTMLElement {
276
- const value = document.createElement('span');
277
- value.className = 'table-value';
278
- const normalized = values.filter((entry) => entry.length > 0);
279
- if (normalized.length === 0) {
280
- value.textContent = '';
281
- return value;
282
- }
283
- for (const rawPart of normalized) {
284
- appendTokenizedValueParts(value, rawPart, 'table-value-part');
285
- }
286
- return value;
287
- }
288
-
289
- private renderHeaderLabel(raw: string): HTMLElement {
290
- const label = document.createElement('span');
291
- label.className = 'table-header-label';
292
- label.style.fontWeight = '400';
293
- if (!isIriValue(raw)) {
294
- label.textContent = raw;
295
- return label;
296
- }
297
- label.appendChild(createWikiLinkElement(raw, shortLabelFromIri(raw)));
298
- return label;
288
+ return { rowKeys, columnKeys, cells, rowHeaders, columnHeaders };
299
289
  }
300
290
 
301
291
  private applyStylesheet(
@@ -365,7 +355,7 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
365
355
  if (!record) {
366
356
  continue;
367
357
  }
368
- const cellText = record.values.join(' ').toLowerCase();
358
+ const cellText = this.formatCellDisplayText(record.cell.value, record.cell).toLowerCase();
369
359
  if (cellText.includes(search)) {
370
360
  matchedRows.add(rowName);
371
361
  matchedColumns.add(columnName);
@@ -400,10 +390,11 @@ export class MatrixMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
400
390
  const lines: string[] = [];
401
391
  lines.push([escapeCell(rowColumnLabel), ...visibleColumns.map(escapeCell)].join(','));
402
392
  for (const rowName of visibleRows) {
403
- const row: string[] = [escapeCell(rowName)];
393
+ const rowHeader = model.rowHeaders.get(rowName);
394
+ const row: string[] = [escapeCell(this.formatCellDisplayText(rowHeader?.value ?? rowName, rowHeader))];
404
395
  for (const columnName of visibleColumns) {
405
396
  const record = model.cells.get(buildMatrixKey(rowName, columnName));
406
- row.push(escapeCell((record?.values ?? []).join(' ')));
397
+ row.push(escapeCell(this.formatCellDisplayText(record?.cell.value ?? '', record?.cell)));
407
398
  }
408
399
  lines.push(row.join(','));
409
400
  }
@@ -424,6 +415,28 @@ function buildMatrixKey(rowKey: string, columnKey: string): string {
424
415
  return `${rowKey}\u0000${columnKey}`;
425
416
  }
426
417
 
418
+ function literalCell(value: string): MdTableCell {
419
+ return { kind: 'literal', value, values: [{ kind: 'literal', value }] };
420
+ }
421
+
422
+ function mergeCellValues(target: MdTableCell, additions: ReadonlyArray<MdTableCellValue>): void {
423
+ const existing = new Set(target.values.map((value) => `${value.kind}\u0000${value.value}`));
424
+ for (const value of additions) {
425
+ const key = `${value.kind}\u0000${value.value}`;
426
+ if (existing.has(key)) {
427
+ continue;
428
+ }
429
+ existing.add(key);
430
+ target.values.push(value);
431
+ }
432
+ target.value = target.values.map((value) => {
433
+ if (value.kind === 'bnode') {
434
+ return `_:${value.value}`;
435
+ }
436
+ return value.value;
437
+ }).join(', ');
438
+ }
439
+
427
440
  function createDownloadButton(): HTMLButtonElement {
428
441
  const downloadButton = document.createElement('button');
429
442
  downloadButton.className = 'tree-download-btn';
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) 2026 Modelware. All rights reserved.
2
2
 
3
- import type { MdBlockExecutionResult } from './types.js';
3
+ import type { MdBlockExecutionResult, MdTableCell, MdTableCellValue } from './types.js';
4
+ import { appendInlineValue, createWikiLinkElement, isUrlValue, shortLabelFromIri } from './wikilink-utils.js';
4
5
 
5
6
  export interface MarkdownBlockRenderer {
6
7
  canRender(result: MdBlockExecutionResult): boolean;
@@ -119,6 +120,43 @@ export abstract class BaseMarkdownBlockRenderer implements MarkdownBlockRenderer
119
120
  }
120
121
 
121
122
  export abstract class QueryMarkdownBlockRenderer extends BaseMarkdownBlockRenderer {
123
+ protected renderCellValue(raw: string, typedValue?: MdTableCell): HTMLElement {
124
+ const value = document.createElement('span');
125
+ value.className = 'table-value';
126
+ const entries = typedValue?.values ?? [];
127
+ if (entries.length > 0) {
128
+ entries.forEach((entry, index) => {
129
+ if (index > 0) {
130
+ value.appendChild(document.createTextNode(' '));
131
+ }
132
+ value.appendChild(this.renderValueToken(entry));
133
+ });
134
+ return value;
135
+ }
136
+ const display = this.formatCellDisplayText(raw, typedValue);
137
+ const fragment = document.createElement('span');
138
+ fragment.className = 'table-value-part';
139
+ fragment.dataset.value = display;
140
+ appendInlineValue(fragment, display);
141
+ value.appendChild(fragment);
142
+ return value;
143
+ }
144
+
145
+ protected formatCellDisplayText(raw: string, typedValue?: MdTableCell): string {
146
+ if (typedValue?.values?.length) {
147
+ return typedValue.values.map((entry) => {
148
+ if (entry.kind === 'iri') {
149
+ return shortLabelFromIri(entry.value);
150
+ }
151
+ if (entry.kind === 'bnode') {
152
+ return `_:${entry.value}`;
153
+ }
154
+ return entry.value;
155
+ }).join(' ');
156
+ }
157
+ return raw;
158
+ }
159
+
122
160
  protected getBlockSource(result: MdBlockExecutionResult): string | undefined {
123
161
  return result.blockSource;
124
162
  }
@@ -130,6 +168,33 @@ export abstract class QueryMarkdownBlockRenderer extends BaseMarkdownBlockRender
130
168
  protected getBlockRange(result: MdBlockExecutionResult): { start?: number; end?: number } {
131
169
  return { start: result.blockLineStart, end: result.blockLineEnd };
132
170
  }
171
+
172
+ private renderValueToken(entry: MdTableCellValue): HTMLElement {
173
+ const fragment = document.createElement('span');
174
+ fragment.className = 'table-value-part';
175
+ fragment.dataset.value = entry.value;
176
+ if (entry.kind === 'iri') {
177
+ if (isBuiltInOntologyIri(entry.value)) {
178
+ appendInlineValue(fragment, shortLabelFromIri(entry.value));
179
+ } else {
180
+ fragment.appendChild(createWikiLinkElement(entry.value, shortLabelFromIri(entry.value)));
181
+ }
182
+ return fragment;
183
+ }
184
+ if (entry.kind === 'bnode') {
185
+ appendInlineValue(fragment, `_:${entry.value}`);
186
+ return fragment;
187
+ }
188
+ if (isUrlValue(entry.value)) {
189
+ const link = document.createElement('a');
190
+ link.href = entry.value;
191
+ link.textContent = entry.value;
192
+ fragment.appendChild(link);
193
+ return fragment;
194
+ }
195
+ appendInlineValue(fragment, entry.value);
196
+ return fragment;
197
+ }
133
198
  }
134
199
 
135
200
  export abstract class CanvasMarkdownBlockRenderer extends QueryMarkdownBlockRenderer {
@@ -148,3 +213,42 @@ export abstract class CanvasMarkdownBlockRenderer extends QueryMarkdownBlockRend
148
213
  return container;
149
214
  }
150
215
  }
216
+
217
+ const BUILT_IN_ONTOLOGY_IRIS = new Set([
218
+ 'http://opencaesar.io/oml',
219
+ 'http://www.w3.org/2001/XMLSchema',
220
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns',
221
+ 'http://www.w3.org/2000/01/rdf-schema',
222
+ 'http://www.w3.org/2002/07/owl',
223
+ 'http://www.w3.org/2003/11/swrl',
224
+ 'http://www.w3.org/2003/11/swrlb',
225
+ ]);
226
+
227
+ function ontologyLookupKeyFromIri(value: string): string | undefined {
228
+ const iri = String(value ?? '').trim().replace(/^<|>$/g, '');
229
+ if (!iri || !/^[A-Za-z][A-Za-z0-9+.-]*:[^\s]+$/.test(iri)) {
230
+ return undefined;
231
+ }
232
+ const hashIndex = iri.indexOf('#');
233
+ if (hashIndex >= 0) {
234
+ return normalizeOntologyLookupKey(iri.slice(0, hashIndex + 1));
235
+ }
236
+ const schemeIndex = iri.indexOf('://');
237
+ const slashIndex = iri.lastIndexOf('/');
238
+ if (slashIndex > schemeIndex + 2) {
239
+ return normalizeOntologyLookupKey(iri.slice(0, slashIndex + 1));
240
+ }
241
+ return normalizeOntologyLookupKey(iri);
242
+ }
243
+
244
+ function normalizeOntologyLookupKey(value: string): string {
245
+ return value.trim().replace(/^<|>$/g, '').replace(/[\/#]+$/, '');
246
+ }
247
+
248
+ function isBuiltInOntologyIri(iri: string): boolean {
249
+ const key = ontologyLookupKeyFromIri(iri);
250
+ if (!key) {
251
+ return false;
252
+ }
253
+ return BUILT_IN_ONTOLOGY_IRIS.has(normalizeOntologyLookupKey(key));
254
+ }