@0m0g1/griot 0.1.2 → 0.1.3

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.
@@ -2,116 +2,133 @@
2
2
  // Renders inline token arrays to either:
3
3
  // a) A DocumentFragment (DOM nodes) — used by Viewer and Editor live preview
4
4
  // b) An HTML string — used for SSR / export
5
- //
6
- // Callbacks:
7
- // onEventClick(eventId) — called when an [[event:]] chip is clicked
8
- // onCiteClick(blockId) — called when a [[cite:]] chip is clicked
9
5
  // ─────────────────────────────────────────────────────────────────────────────
10
6
 
11
7
  import { tokenizeInline, TOKEN } from './InlineLexer.js';
12
8
 
13
- // ─── DOM rendering ────────────────────────────────────────────────────────────
9
+ // ── DOM rendering ─────────────────────────────────────────────────────────────
10
+
14
11
  export function renderInlineToDOM(text = '', { onEventClick, onCiteClick } = {}) {
15
12
  const frag = document.createDocumentFragment();
16
- const tokens = tokenizeInline(text);
17
-
18
- for (const t of tokens) {
19
- let node;
20
-
21
- switch (t.type) {
22
- case TOKEN.TEXT:
23
- node = document.createTextNode(t.text);
24
- break;
25
-
26
- case TOKEN.BOLD:
27
- node = document.createElement('strong');
28
- node.textContent = t.text;
29
- break;
30
-
31
- case TOKEN.ITALIC:
32
- node = document.createElement('em');
33
- node.textContent = t.text;
34
- break;
35
-
36
- case TOKEN.CODE: {
37
- node = document.createElement('code');
38
- node.className = 'griot-inline-code';
39
- node.textContent = t.text;
40
- break;
41
- }
42
-
43
- case TOKEN.LINK: {
44
- node = document.createElement('a');
45
- node.href = t.href;
46
- node.target = '_blank';
47
- node.rel = 'noopener noreferrer';
48
- node.className = 'griot-link';
49
- node.textContent = t.text;
50
- break;
51
- }
13
+ for (const t of tokenizeInline(text)) {
14
+ frag.appendChild(_toNode(t, { onEventClick, onCiteClick }));
15
+ }
16
+ return frag;
17
+ }
52
18
 
53
- case TOKEN.EVENT_REF: {
54
- node = document.createElement('button');
55
- node.type = 'button';
56
- node.className = 'griot-chip griot-chip--event';
57
- node.dataset.eventId = t.eventId;
58
- node.innerHTML = `<span class="griot-chip__icon">⏱</span><span class="griot-chip__label">${escHtml(t.label)}</span>`;
59
- if (onEventClick) {
60
- node.addEventListener('click', (e) => {
61
- e.stopPropagation();
62
- onEventClick(t.eventId);
63
- });
64
- }
65
- break;
66
- }
19
+ function _toNode(t, opts) {
20
+ switch (t.type) {
67
21
 
68
- case TOKEN.CITE_REF: {
69
- node = document.createElement('button');
70
- node.type = 'button';
71
- node.className = 'griot-chip griot-chip--cite';
72
- node.dataset.blockId = t.blockId;
73
- node.innerHTML = `<span class="griot-chip__icon">📖</span><span class="griot-chip__label">${escHtml(t.label)}</span>`;
74
- if (onCiteClick) {
75
- node.addEventListener('click', (e) => {
76
- e.stopPropagation();
77
- onCiteClick(t.blockId);
78
- });
79
- }
80
- break;
81
- }
22
+ case TOKEN.TEXT:
23
+ return document.createTextNode(t.text);
82
24
 
83
- default:
84
- node = document.createTextNode(t.text ?? '');
25
+ case TOKEN.BOLD: {
26
+ const el = document.createElement('strong');
27
+ el.textContent = t.text;
28
+ return el;
29
+ }
30
+ case TOKEN.ITALIC: {
31
+ const el = document.createElement('em');
32
+ el.textContent = t.text;
33
+ return el;
34
+ }
35
+ case TOKEN.UNDERLINE: {
36
+ const el = document.createElement('u');
37
+ el.className = 'griot-underline';
38
+ el.textContent = t.text;
39
+ return el;
40
+ }
41
+ case TOKEN.STRIKE: {
42
+ const el = document.createElement('s');
43
+ el.className = 'griot-strike';
44
+ el.textContent = t.text;
45
+ return el;
46
+ }
47
+ case TOKEN.HIGHLIGHT: {
48
+ const el = document.createElement('mark');
49
+ el.className = 'griot-highlight';
50
+ el.textContent = t.text;
51
+ return el;
52
+ }
53
+ case TOKEN.COLOR_MARK: {
54
+ const el = document.createElement('span');
55
+ el.className = 'griot-color-mark';
56
+ el.style.color = t.color;
57
+ el.textContent = t.text;
58
+ return el;
59
+ }
60
+ case TOKEN.CODE: {
61
+ const el = document.createElement('code');
62
+ el.className = 'griot-inline-code';
63
+ el.textContent = t.code;
64
+ return el;
65
+ }
66
+ case TOKEN.IMAGE: {
67
+ const el = document.createElement('img');
68
+ el.src = t.src;
69
+ el.alt = t.alt ?? '';
70
+ el.className = 'griot-inline-img';
71
+ return el;
72
+ }
73
+ case TOKEN.LINK: {
74
+ const el = document.createElement('a');
75
+ el.href = t.href;
76
+ el.target = '_blank';
77
+ el.rel = 'noopener noreferrer';
78
+ el.className = 'griot-link';
79
+ el.textContent = t.label;
80
+ return el;
81
+ }
82
+ case TOKEN.EVENT_REF: {
83
+ const el = document.createElement('button');
84
+ el.type = 'button';
85
+ el.className = 'griot-chip griot-chip--event';
86
+ el.dataset.eventId = t.eventId;
87
+ el.innerHTML = `<span class="griot-chip__icon">⏱</span><span class="griot-chip__label">${escHtml(t.label)}</span>`;
88
+ if (opts.onEventClick) el.addEventListener('click', (e) => { e.stopPropagation(); opts.onEventClick(t.eventId); });
89
+ return el;
90
+ }
91
+ case TOKEN.CITE_REF: {
92
+ const el = document.createElement('button');
93
+ el.type = 'button';
94
+ el.className = 'griot-chip griot-chip--cite';
95
+ el.dataset.blockId = t.blockId;
96
+ el.innerHTML = `<span class="griot-chip__icon">📖</span><span class="griot-chip__label">${escHtml(t.label)}</span>`;
97
+ if (opts.onCiteClick) el.addEventListener('click', (e) => { e.stopPropagation(); opts.onCiteClick(t.blockId); });
98
+ return el;
85
99
  }
86
100
 
87
- frag.appendChild(node);
101
+ default:
102
+ return document.createTextNode(t.text ?? '');
88
103
  }
89
-
90
- return frag;
91
104
  }
92
105
 
93
- // ─── HTML string rendering ────────────────────────────────────────────────────
106
+ // ── HTML string rendering ─────────────────────────────────────────────────────
107
+
94
108
  export function renderInlineToHTML(text = '') {
95
- const tokens = tokenizeInline(text);
96
- let html = '';
109
+ return tokenizeInline(text).map(_toHTML).join('');
110
+ }
97
111
 
98
- for (const t of tokens) {
99
- switch (t.type) {
100
- case TOKEN.TEXT: html += escHtml(t.text); break;
101
- case TOKEN.BOLD: html += `<strong>${escHtml(t.text)}</strong>`; break;
102
- case TOKEN.ITALIC: html += `<em>${escHtml(t.text)}</em>`; break;
103
- case TOKEN.CODE: html += `<code class="griot-inline-code">${escHtml(t.text)}</code>`; break;
104
- case TOKEN.LINK: html += `<a class="griot-link" href="${escAttr(t.href)}" target="_blank" rel="noopener noreferrer">${escHtml(t.text)}</a>`; break;
105
- case TOKEN.EVENT_REF: html += `<button type="button" class="griot-chip griot-chip--event" data-event-id="${escAttr(t.eventId)}"><span class="griot-chip__icon">⏱</span><span class="griot-chip__label">${escHtml(t.label)}</span></button>`; break;
106
- case TOKEN.CITE_REF: html += `<button type="button" class="griot-chip griot-chip--cite" data-block-id="${escAttr(t.blockId)}"><span class="griot-chip__icon">📖</span><span class="griot-chip__label">${escHtml(t.label)}</span></button>`; break;
107
- default: html += escHtml(t.text ?? '');
108
- }
112
+ function _toHTML(t) {
113
+ switch (t.type) {
114
+ case TOKEN.TEXT: return escHtml(t.text);
115
+ case TOKEN.BOLD: return `<strong>${escHtml(t.text)}</strong>`;
116
+ case TOKEN.ITALIC: return `<em>${escHtml(t.text)}</em>`;
117
+ case TOKEN.UNDERLINE: return `<u class="griot-underline">${escHtml(t.text)}</u>`;
118
+ case TOKEN.STRIKE: return `<s class="griot-strike">${escHtml(t.text)}</s>`;
119
+ case TOKEN.HIGHLIGHT: return `<mark class="griot-highlight">${escHtml(t.text)}</mark>`;
120
+ case TOKEN.COLOR_MARK: return `<span class="griot-color-mark" style="color:${escAttr(t.color)}">${escHtml(t.text)}</span>`;
121
+ case TOKEN.CODE: return `<code class="griot-inline-code">${escHtml(t.code)}</code>`;
122
+ case TOKEN.IMAGE: return `<img class="griot-inline-img" src="${escAttr(t.src)}" alt="${escAttr(t.alt ?? '')}">`;
123
+ case TOKEN.LINK: return `<a class="griot-link" href="${escAttr(t.href)}" target="_blank" rel="noopener noreferrer">${escHtml(t.label)}</a>`;
124
+ case TOKEN.EVENT_REF: return `<button type="button" class="griot-chip griot-chip--event" data-event-id="${escAttr(t.eventId)}"><span class="griot-chip__icon">⏱</span><span class="griot-chip__label">${escHtml(t.label)}</span></button>`;
125
+ case TOKEN.CITE_REF: return `<button type="button" class="griot-chip griot-chip--cite" data-block-id="${escAttr(t.blockId)}"><span class="griot-chip__icon">📖</span><span class="griot-chip__label">${escHtml(t.label)}</span></button>`;
126
+ default: return escHtml(t.text ?? '');
109
127
  }
110
-
111
- return html;
112
128
  }
113
129
 
114
- // ─── Escape helpers ───────────────────────────────────────────────────────────
130
+ // ── Escape helpers ────────────────────────────────────────────────────────────
131
+
115
132
  export function escHtml(s) {
116
133
  return String(s ?? '')
117
134
  .replace(/&/g, '&amp;')
@@ -122,7 +139,5 @@ export function escHtml(s) {
122
139
  }
123
140
 
124
141
  export function escAttr(s) {
125
- return String(s ?? '')
126
- .replace(/&/g, '&amp;')
127
- .replace(/"/g, '&quot;');
128
- }
142
+ return String(s ?? '').replace(/&/g, '&amp;').replace(/"/g, '&quot;');
143
+ }