kmap-term-tree 0.9.20 → 0.9.22

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/demo/index.html +124 -10
  2. package/dist/src/KmapTermTree.d.ts +30 -47
  3. package/dist/src/KmapTermTree.js +536 -221
  4. package/dist/src/KmapTermTree.js.map +1 -1
  5. package/dist/src/asciimath-parser.d.ts +17 -0
  6. package/dist/src/asciimath-parser.js +472 -0
  7. package/dist/src/asciimath-parser.js.map +1 -0
  8. package/dist/src/index.d.ts +2 -0
  9. package/dist/src/index.js +2 -0
  10. package/dist/src/index.js.map +1 -1
  11. package/dist/src/katex-css.d.ts +2 -0
  12. package/dist/src/katex-css.js +7 -0
  13. package/dist/src/katex-css.js.map +1 -0
  14. package/dist/src/model.d.ts +30 -0
  15. package/dist/src/model.js +26 -0
  16. package/dist/src/model.js.map +1 -0
  17. package/dist/src/parser.js.map +1 -1
  18. package/dist/src/tokenizer.d.ts +2 -2
  19. package/dist/src/tokenizer.js.map +1 -1
  20. package/dist/stories/index.stories.d.ts +20 -0
  21. package/dist/stories/index.stories.js +26 -2
  22. package/dist/stories/index.stories.js.map +1 -1
  23. package/dist/test/asciimath-parser.test.d.ts +1 -0
  24. package/dist/test/asciimath-parser.test.js +45 -0
  25. package/dist/test/asciimath-parser.test.js.map +1 -0
  26. package/dist/test/kmap-term-tree.test.js +30 -7
  27. package/dist/test/kmap-term-tree.test.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +19 -53
  30. package/src/KmapTermTree.ts +557 -235
  31. package/src/asciimath-parser.ts +554 -0
  32. package/src/index.ts +2 -0
  33. package/src/katex-css.ts +7 -0
  34. package/src/model.ts +57 -0
  35. package/stories/index.stories.ts +35 -2
  36. package/test/asciimath-parser.test.ts +50 -0
  37. package/test/kmap-term-tree.test.ts +44 -7
@@ -1,273 +1,588 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { css, html, LitElement, svg } from 'lit';
3
3
  import { property, state, query } from 'lit/decorators.js';
4
- import { ifDefined } from 'lit/directives/if-defined.js';
5
- import { Parser } from "./parser.js";
6
- import { Token } from "./tokenizer.js";
4
+ import katex from 'katex';
5
+ import { katexStyles } from './katex-css.js';
6
+ import { parseAsciiMath, astToAnnotatedLatex } from './asciimath-parser.js';
7
7
  export class KmapTermTree extends LitElement {
8
8
  constructor() {
9
9
  super(...arguments);
10
- this.parser = new Parser();
11
- this.tension = 0;
10
+ this.tension = 0.5;
11
+ this.showLabels = false;
12
+ this.showDetails = true;
13
+ this.flatNodes = [];
14
+ this.edges = [];
15
+ this.activeSubtreeIds = new Set();
16
+ this.treeHeight = 100;
12
17
  }
13
- // @ts-ignore
14
- updateSlotted({ target }) {
15
- // @ts-ignore
16
- this.term = target.assignedNodes().map((n) => n.textContent).join('');
18
+ connectedCallback() {
19
+ super.connectedCallback();
20
+ this.resizeObserver = new ResizeObserver(() => {
21
+ this.recalculateLayout();
22
+ });
17
23
  }
18
- update(_changedProperties) {
19
- if (_changedProperties.has("term")) {
20
- if (this.term !== undefined) {
21
- this.tokens = Token.tokenize(this.term);
22
- this.termNode = this.parser.parse(this.tokens);
23
- let map = {};
24
- this.maxDepths = this.calcDepths(this.termNode, map);
25
- let connections = [];
26
- this.termNode.breadthFirst((n) => {
27
- if (n.leftChildNode !== undefined)
28
- connections.push({ from: n.token.id, to: n.leftChildNode.token.id });
29
- if (n.rightChildNode !== undefined)
30
- connections.push({ from: n.token.id, to: n.rightChildNode.token.id });
31
- });
32
- this.depths = map;
33
- this.connections = connections;
34
- }
24
+ disconnectedCallback() {
25
+ var _a;
26
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
27
+ super.disconnectedCallback();
28
+ }
29
+ // Support slotted content: <kmap-term-tree>2a+3b</kmap-term-tree>
30
+ updateSlotted(event) {
31
+ const slot = event.target;
32
+ const text = slot
33
+ .assignedNodes()
34
+ .map(n => n.textContent)
35
+ .join('')
36
+ .trim();
37
+ if (text && text !== this.term) {
38
+ this.term = text;
35
39
  }
36
- super.update(_changedProperties);
37
40
  }
38
- calcDepths(node, map) {
39
- let depth = 0;
40
- if (node.leftChildNode)
41
- depth = Math.max(depth, this.calcDepths(node.leftChildNode, map) + 1);
42
- if (node.rightChildNode)
43
- depth = Math.max(depth, this.calcDepths(node.rightChildNode, map) + 1);
44
- map[node.token.id] = depth;
45
- return depth;
41
+ willUpdate(changedProperties) {
42
+ if (changedProperties.has('term') && this.term !== undefined) {
43
+ this.parseInput(this.term);
44
+ }
46
45
  }
47
- render() {
48
- return html `
49
- ${this.connections ? html `
50
- <kmap-term-tree-edges .tension="${this.tension}"></kmap-term-tree-edges>
51
- ` : ''}
52
- ${!this.tokens ? '' : html `
53
- <div class="tokens" id="tokens">
54
- ${this.tokens.map((t) => html `
55
- <div class="token" id="${t.id}">${KmapTermTree._prettify(t.value)}</div>
56
- `)}
57
- </div>
58
- <div class="notokens">
59
- ${this.tokens.map((t) => html `
60
- <div>${KmapTermTree._prettify(t.value)}</div>
61
- `)}
62
- </div>
63
- `}
64
- ${this.tokens && this.depths ? html `
65
- <div class="nodes">
66
- ${this.tokens.map((t) => html `
67
- <div class="node" id="n${t.id}" depth="${ifDefined(this.depths ? this.depths[t.id] : undefined)}">${KmapTermTree._prettify(t.value)}</div>
68
- `)}
69
- </div>
70
- ` : ''}
71
- <div hidden>
72
- <slot @slotchange=${this.updateSlotted}></slot>
73
- </div>
74
- `;
46
+ parseInput(termStr) {
47
+ try {
48
+ this.error = undefined;
49
+ const cleanTerm = termStr.trim();
50
+ if (!cleanTerm) {
51
+ this.ast = undefined;
52
+ this.flatNodes = [];
53
+ this.edges = [];
54
+ return;
55
+ }
56
+ this.ast = parseAsciiMath(cleanTerm);
57
+ this.activeNodeId = this.ast.id;
58
+ this.activeSubtreeIds = this.collectSubtreeIds(this.ast);
59
+ }
60
+ catch (e) {
61
+ this.error = e.message || 'Fehler beim Parsen des Terms';
62
+ this.ast = undefined;
63
+ this.flatNodes = [];
64
+ this.edges = [];
65
+ }
75
66
  }
76
- async updated(_changedProperties) {
77
- if (_changedProperties.has("connections")) {
67
+ async updated(changedProperties) {
68
+ if (changedProperties.has('term') || changedProperties.has('ast')) {
69
+ this.renderKaTeX();
78
70
  await this.updateComplete;
79
- if (this.tokensElement && this.maxDepths) {
80
- let tokensHeight = this.tokensElement.offsetHeight;
81
- //console.log(this.maxDepths)
82
- //console.log(tokensHeight + "px")
83
- this.style.setProperty("--kmap-term-tree-max-depth", "" + this.maxDepths);
84
- this.style.setProperty("--kmap-term-tree-vertical-distance", tokensHeight + "px");
85
- this.style.height = "calc(3px + var(--kmap-term-tree-vertical-distance) * (var(--kmap-term-tree-max-depth)*1.5 + 1))";
86
- //console.log(this.style.height);
87
- }
88
- let edges = [];
89
- if (this.edgesElement && this.connections) {
90
- for (const connection of this.connections) {
91
- let edge = this._connect(connection);
92
- if (edge)
93
- edges.push(edge);
94
- }
95
- this.edgesElement.edges = edges;
71
+ this.recalculateLayout();
72
+ if (this.treeCanvasElement && this.resizeObserver) {
73
+ this.resizeObserver.observe(this.treeCanvasElement);
96
74
  }
97
75
  }
98
76
  }
99
- _connect(connection) {
100
- let from = this.shadowRoot.getElementById("n" + connection.from);
101
- let to = this.shadowRoot.getElementById("n" + connection.to);
102
- if (!from || !to)
77
+ renderKaTeX() {
78
+ if (!this.formulaElement || !this.ast)
103
79
  return;
104
- let frompos = this._findAbsolutePosition(from);
105
- let topos = this._findAbsolutePosition(to);
106
- return { from: frompos, to: topos };
107
- }
108
- _findAbsolutePosition(element) {
109
- var x = 0;
110
- var y = 0;
111
- var el = element;
112
- for (; el !== this; el = el.offsetParent) {
113
- x += el.offsetLeft;
114
- y += el.offsetTop;
80
+ try {
81
+ const latex = astToAnnotatedLatex(this.ast);
82
+ katex.render(latex, this.formulaElement, {
83
+ trust: true,
84
+ strict: false,
85
+ throwOnError: false,
86
+ });
115
87
  }
116
- return {
117
- x: x,
118
- y: y,
119
- width: element.offsetWidth,
120
- height: element.offsetHeight,
121
- };
122
- }
123
- static _prettify(value) {
124
- switch (value) {
125
- case '*':
126
- return "·";
127
- case '-':
128
- return "−"; // math minus
129
- default:
130
- return value;
88
+ catch (err) {
89
+ this.error = err.message;
131
90
  }
132
91
  }
133
- }
134
- // language=CSS
135
- KmapTermTree.styles = css `
136
- :host {
137
- display: block;
138
- color: var(--kmap-term-tree-text-color, #000);
139
- position: relative;
140
- }
141
- .tokens, .nodes, kmap-term-tree-edges {
142
- position: absolute;
143
- }
144
- .tokens, .nodes {
145
- white-space: nowrap;
146
- }
147
- .notokens {
148
- white-space: nowrap;
149
- height: 0px;
150
- }
151
- .notokens div {
152
- display: inline-block;
153
- }
154
- kmap-term-tree-edges {
155
- width: 100%;
156
- height: 100%;
157
- }
158
- .token {
159
- display: inline-block;
160
- }
161
- .token, .node {
162
- display: inline-block;
163
- position: relative;
164
- background-color: var(--kmap-term-tree-background-color, white);
92
+ recalculateLayout() {
93
+ if (!this.ast ||
94
+ !this.formulaElement ||
95
+ !this.treeCanvasElement ||
96
+ !this.shadowRoot)
97
+ return;
98
+ const canvasRect = this.treeCanvasElement.getBoundingClientRect();
99
+ if (canvasRect.width === 0)
100
+ return;
101
+ const flatNodes = [];
102
+ const nodeMap = new Map();
103
+ // 1. Collect all nodes and measure leaf positions from KaTeX DOM elements
104
+ const collectAndPositionLeaves = (node) => {
105
+ nodeMap.set(node.id, node);
106
+ if (node.children.length === 0) {
107
+ // Leaf node: measure rendered KaTeX span
108
+ const el = this.shadowRoot.getElementById(node.id);
109
+ if (el) {
110
+ const rect = el.getBoundingClientRect();
111
+ node.x = rect.left - canvasRect.left + rect.width / 2;
112
+ node.y = rect.bottom - canvasRect.top;
113
+ node.width = rect.width;
114
+ node.height = rect.height;
115
+ }
116
+ else {
117
+ node.x = canvasRect.width / 2;
118
+ node.y = 20;
119
+ }
120
+ }
121
+ else {
122
+ flatNodes.push(node);
123
+ for (const child of node.children) {
124
+ collectAndPositionLeaves(child);
125
+ }
126
+ }
127
+ };
128
+ collectAndPositionLeaves(this.ast);
129
+ // 2. Position inner nodes bottom-up based on children coordinates
130
+ const formulaRect = this.formulaElement.getBoundingClientRect();
131
+ const formulaBottomRel = formulaRect.bottom - canvasRect.top;
132
+ const initialGap = 34; // distance from formula bottom to level 1 operators
133
+ const rowHeight = 40; // distance between tree levels
134
+ const positionInnerNodes = (node) => {
135
+ var _a, _b, _c;
136
+ for (const child of node.children) {
137
+ if (child.children.length > 0) {
138
+ positionInnerNodes(child);
139
+ }
140
+ }
141
+ if (node.children.length > 0) {
142
+ if (node.type === 'function' || node.isUnary) {
143
+ // Center under the full function / unary expression: e.g. sin(arg) or -arg
144
+ const el = this.shadowRoot.getElementById(node.id);
145
+ if (el) {
146
+ const rect = el.getBoundingClientRect();
147
+ node.x = rect.left - canvasRect.left + rect.width / 2;
148
+ }
149
+ else {
150
+ node.x = (_a = node.children[0].x) !== null && _a !== void 0 ? _a : 0;
151
+ }
152
+ }
153
+ else {
154
+ const first = node.children[0];
155
+ const last = node.children[node.children.length - 1];
156
+ node.x = (((_b = first.x) !== null && _b !== void 0 ? _b : 0) + ((_c = last.x) !== null && _c !== void 0 ? _c : 0)) / 2;
157
+ }
158
+ node.y = formulaBottomRel + initialGap + (node.depth - 1) * rowHeight;
159
+ }
160
+ };
161
+ positionInnerNodes(this.ast);
162
+ // 3. Build edges between parent nodes and their children
163
+ const edges = [];
164
+ for (const node of flatNodes) {
165
+ if (node.x === undefined || node.y === undefined)
166
+ continue;
167
+ for (const child of node.children) {
168
+ if (child.x === undefined || child.y === undefined)
169
+ continue;
170
+ edges.push({
171
+ fromNodeId: child.id,
172
+ toNodeId: node.id,
173
+ fromX: child.x,
174
+ fromY: child.y + (child.depth > 0 ? 14 : 2),
175
+ toX: node.x,
176
+ toY: node.y - 14,
177
+ });
178
+ }
179
+ }
180
+ const maxDepth = this.ast.depth;
181
+ this.treeHeight = Math.max(60, formulaBottomRel + initialGap + (maxDepth - 1) * rowHeight + 24);
182
+ this.flatNodes = flatNodes;
183
+ this.edges = edges;
184
+ this.updateHighlightRect(this.activeNodeId);
185
+ this.requestUpdate();
165
186
  }
166
- .node {
167
- display: inline-block;
168
- position: relative;
169
- border: 1px solid var(--kmap-term-tree-border-color, #005b9f);
170
- padding: 0px 4px;
171
- border-radius: 1em;
172
- margin: -1px -5px;
187
+ collectSubtreeIds(node) {
188
+ const ids = new Set();
189
+ const traverse = (n) => {
190
+ ids.add(n.id);
191
+ for (const c of n.children)
192
+ traverse(c);
193
+ };
194
+ traverse(node);
195
+ return ids;
173
196
  }
174
- .node[depth="0"], .node:not([depth]) {
175
- visibility: hidden;
197
+ onNodeHover(node, enter) {
198
+ if (!enter) {
199
+ // Revert highlight to root node
200
+ if (this.ast) {
201
+ this.setActiveNode(this.ast);
202
+ }
203
+ return;
204
+ }
205
+ this.setActiveNode(node);
176
206
  }
177
- .node[depth="1"] {
178
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 1.5);
207
+ setActiveNode(node) {
208
+ this.activeNodeId = node.id;
209
+ this.activeSubtreeIds = this.collectSubtreeIds(node);
210
+ this.updateHighlightRect(node.id);
211
+ this.requestUpdate();
179
212
  }
180
- .node[depth="2"] {
181
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 3);
213
+ findNode(root, id) {
214
+ if (!root)
215
+ return undefined;
216
+ if (root.id === id)
217
+ return root;
218
+ for (const child of root.children) {
219
+ const found = this.findNode(child, id);
220
+ if (found)
221
+ return found;
222
+ }
223
+ return undefined;
182
224
  }
183
- .node[depth="3"] {
184
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 4.5);
225
+ getNodeVisualBounds(node) {
226
+ if (!this.shadowRoot)
227
+ return null;
228
+ let minTop = Infinity;
229
+ let maxBottom = -Infinity;
230
+ let minLeft = Infinity;
231
+ let maxRight = -Infinity;
232
+ const collectBounds = (n) => {
233
+ const el = this.shadowRoot.getElementById(n.id);
234
+ if (el) {
235
+ const r = el.getBoundingClientRect();
236
+ if (r.width > 0 && r.height > 0) {
237
+ minTop = Math.min(minTop, r.top);
238
+ maxBottom = Math.max(maxBottom, r.bottom);
239
+ minLeft = Math.min(minLeft, r.left);
240
+ maxRight = Math.max(maxRight, r.right);
241
+ }
242
+ }
243
+ for (const child of n.children) {
244
+ collectBounds(child);
245
+ }
246
+ };
247
+ collectBounds(node);
248
+ if (minTop === Infinity)
249
+ return null;
250
+ return { top: minTop, bottom: maxBottom, left: minLeft, right: maxRight };
185
251
  }
186
- .node[depth="4"] {
187
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 6);
252
+ updateHighlightRect(nodeId) {
253
+ if (!nodeId || !this.shadowRoot || !this.treeCanvasElement || !this.ast) {
254
+ this.highlightRect = undefined;
255
+ return;
256
+ }
257
+ const node = this.findNode(this.ast, nodeId);
258
+ if (!node) {
259
+ this.highlightRect = undefined;
260
+ return;
261
+ }
262
+ const bounds = this.getNodeVisualBounds(node);
263
+ if (!bounds) {
264
+ this.highlightRect = undefined;
265
+ return;
266
+ }
267
+ const canvasRect = this.treeCanvasElement.getBoundingClientRect();
268
+ const paddingX = 4;
269
+ const paddingY = 4;
270
+ this.highlightRect = {
271
+ left: bounds.left - canvasRect.left - paddingX,
272
+ top: bounds.top - canvasRect.top - paddingY,
273
+ width: bounds.right - bounds.left + paddingX * 2,
274
+ height: bounds.bottom - bounds.top + paddingY * 2,
275
+ };
188
276
  }
189
- .node[depth="5"] {
190
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 7.5);
277
+ renderPath(edge) {
278
+ const delta = (edge.toY - edge.fromY) * this.tension;
279
+ const hx1 = edge.fromX;
280
+ const hy1 = edge.fromY + delta;
281
+ const hx2 = edge.toX;
282
+ const hy2 = edge.toY - delta;
283
+ return `M ${edge.fromX} ${edge.fromY} C ${hx1} ${hy1} ${hx2} ${hy2} ${edge.toX} ${edge.toY}`;
191
284
  }
192
- .node[depth="6"] {
193
- top: calc(var(--kmap-term-tree-vertical-distance, 1.5em) * 9);
285
+ render() {
286
+ var _a;
287
+ const activeNode = this.flatNodes.find(n => n.id === this.activeNodeId) || this.ast;
288
+ const isRoot = (activeNode === null || activeNode === void 0 ? void 0 : activeNode.id) === ((_a = this.ast) === null || _a === void 0 ? void 0 : _a.id);
289
+ return html `
290
+ <div class="container">
291
+ <!-- Canvas area with formula, edges, and operator nodes sharing the same coordinate space -->
292
+ <div class="tree-canvas" style="height: ${this.treeHeight}px">
293
+ ${this.highlightRect
294
+ ? html `
295
+ <div
296
+ class="highlight-overlay"
297
+ style="left: ${this.highlightRect.left}px; top: ${this
298
+ .highlightRect.top}px; width: ${this.highlightRect
299
+ .width}px; height: ${this.highlightRect.height}px;"
300
+ ></div>
301
+ `
302
+ : ''}
303
+
304
+ <div class="formula-wrapper">
305
+ <div id="formula"></div>
306
+ </div>
307
+
308
+ ${this.error
309
+ ? html `<div class="error-msg">${this.error}</div>`
310
+ : html `
311
+ <svg class="edges-svg">
312
+ ${this.edges.map(edge => svg `
313
+ <path
314
+ class="edge-path ${this.activeSubtreeIds.has(edge.fromNodeId) &&
315
+ this.activeSubtreeIds.has(edge.toNodeId)
316
+ ? 'highlighted'
317
+ : ''}"
318
+ d="${this.renderPath(edge)}"
319
+ />
320
+ `)}
321
+ </svg>
322
+
323
+ ${this.flatNodes.map(node => html `
324
+ <div
325
+ class="tree-node ${this.activeNodeId === node.id
326
+ ? 'active'
327
+ : ''}"
328
+ style="left: ${node.x}px; top: ${node.y}px;"
329
+ @mouseenter=${() => this.onNodeHover(node, true)}
330
+ @mouseleave=${() => this.onNodeHover(node, false)}
331
+ @click=${() => this.setActiveNode(node)}
332
+ >
333
+ <div class="node-badge" title="${node.label}">
334
+ ${node.symbol}
335
+ </div>
336
+ ${this.showLabels
337
+ ? html `<span class="node-label">${node.label}</span>`
338
+ : ''}
339
+ </div>
340
+ `)}
341
+ `}
342
+ </div>
343
+
344
+ <!-- Didactic info box -->
345
+ ${this.showDetails && activeNode
346
+ ? html `
347
+ <div class="info-bar">
348
+ <span>${isRoot ? 'Hauptrechenart:' : 'Teilterm:'}</span>
349
+ <span class="info-badge">${activeNode.label}</span>
350
+ <span style="color: #64748b">
351
+ ${activeNode.children.length === 2
352
+ ? `(verknüpft 2 Teilterme mit ${activeNode.symbol})`
353
+ : activeNode.isUnary
354
+ ? `(wirkt auf ${activeNode.children[0].label || 'Ausdruck'})`
355
+ : ''}
356
+ </span>
357
+ </div>
358
+ `
359
+ : ''}
360
+
361
+ <div hidden>
362
+ <slot @slotchange=${this.updateSlotted}></slot>
363
+ </div>
364
+ </div>
365
+ `;
194
366
  }
195
- `;
367
+ }
368
+ KmapTermTree.styles = [
369
+ katexStyles,
370
+ css `
371
+ :host {
372
+ display: block;
373
+ position: relative;
374
+ font-family: var(
375
+ --kmap-term-tree-font-family,
376
+ system-ui,
377
+ -apple-system,
378
+ sans-serif
379
+ );
380
+ color: var(--kmap-term-tree-text-color, #1f2937);
381
+ user-select: none;
382
+ width: 100%;
383
+ }
384
+
385
+ .container {
386
+ position: relative;
387
+ display: flex;
388
+ flex-direction: column;
389
+ align-items: center;
390
+ width: 100%;
391
+ box-sizing: border-box;
392
+ }
393
+
394
+ .tree-canvas {
395
+ position: relative;
396
+ width: 100%;
397
+ min-height: 80px;
398
+ }
399
+
400
+ .formula-wrapper {
401
+ position: relative;
402
+ z-index: 2;
403
+ display: flex;
404
+ justify-content: center;
405
+ align-items: center;
406
+ padding-top: 4px;
407
+ font-size: var(--kmap-term-tree-formula-font-size, 1.35rem);
408
+ }
409
+
410
+ .highlight-overlay {
411
+ position: absolute;
412
+ z-index: 1;
413
+ pointer-events: none;
414
+ background-color: var(
415
+ --kmap-term-tree-highlight-color,
416
+ rgba(251, 191, 36, 0.28)
417
+ );
418
+ border: 2px solid
419
+ var(--kmap-term-tree-highlight-outline, rgba(245, 158, 11, 0.75));
420
+ border-radius: 6px;
421
+ box-sizing: border-box;
422
+ transition: left 0.12s ease-out, top 0.12s ease-out,
423
+ width 0.12s ease-out, height 0.12s ease-out;
424
+ }
425
+
426
+ .edges-svg {
427
+ position: absolute;
428
+ top: 0;
429
+ left: 0;
430
+ width: 100%;
431
+ height: 100%;
432
+ pointer-events: none;
433
+ z-index: 1;
434
+ overflow: visible;
435
+ }
436
+
437
+ .edge-path {
438
+ fill: none;
439
+ stroke: var(--kmap-term-tree-edge-color, #f59e0b);
440
+ stroke-width: 2.2;
441
+ stroke-linecap: round;
442
+ opacity: 0.85;
443
+ transition: stroke 0.2s ease, stroke-width 0.2s ease, opacity 0.2s ease;
444
+ }
445
+
446
+ .edge-path.highlighted {
447
+ stroke: var(--kmap-term-tree-highlight-edge-color, #d97706);
448
+ stroke-width: 3.2;
449
+ opacity: 1;
450
+ }
451
+
452
+ .tree-node {
453
+ position: absolute;
454
+ z-index: 2;
455
+ transform: translate(-50%, -50%);
456
+ display: inline-flex;
457
+ flex-direction: column;
458
+ align-items: center;
459
+ cursor: pointer;
460
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
461
+ }
462
+
463
+ .tree-node:hover,
464
+ .tree-node.active {
465
+ transform: translate(-50%, -50%) scale(1.15);
466
+ }
467
+
468
+ .node-badge {
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: center;
472
+ width: 28px;
473
+ height: 28px;
474
+ border-radius: 50%;
475
+ background: var(--kmap-term-tree-background-color, #ffffff);
476
+ border: 2px solid var(--kmap-term-tree-border-color, #0284c7);
477
+ color: var(--kmap-term-tree-node-text-color, #0284c7);
478
+ font-weight: 700;
479
+ font-size: 1rem;
480
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
481
+ box-sizing: border-box;
482
+ transition: all 0.2s ease;
483
+ }
484
+
485
+ .tree-node:hover .node-badge,
486
+ .tree-node.active .node-badge {
487
+ background: var(--kmap-term-tree-active-node-bg, #0284c7);
488
+ color: #ffffff;
489
+ border-color: var(--kmap-term-tree-active-node-border, #0284c7);
490
+ box-shadow: 0 4px 8px rgba(2, 132, 199, 0.35);
491
+ }
492
+
493
+ .node-label {
494
+ margin-top: 2px;
495
+ font-size: 0.7rem;
496
+ font-weight: 500;
497
+ color: #64748b;
498
+ white-space: nowrap;
499
+ background: rgba(255, 255, 255, 0.95);
500
+ padding: 1px 4px;
501
+ border-radius: 4px;
502
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
503
+ }
504
+
505
+ /* KaTeX Highlight */
506
+ .highlight-term {
507
+ background-color: var(
508
+ --kmap-term-tree-highlight-color,
509
+ rgba(251, 191, 36, 0.35)
510
+ ) !important;
511
+ border-radius: 4px;
512
+ outline: 2px solid
513
+ var(--kmap-term-tree-highlight-outline, rgba(245, 158, 11, 0.7));
514
+ transition: background-color 0.2s ease, outline 0.2s ease;
515
+ }
516
+
517
+ .info-bar {
518
+ margin-top: 12px;
519
+ padding: 6px 14px;
520
+ background: #f8fafc;
521
+ border: 1px solid #e2e8f0;
522
+ border-radius: 8px;
523
+ font-size: 0.88rem;
524
+ display: flex;
525
+ align-items: center;
526
+ gap: 8px;
527
+ color: #334155;
528
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
529
+ }
530
+
531
+ .info-badge {
532
+ font-weight: 700;
533
+ color: #0284c7;
534
+ background: #e0f2fe;
535
+ padding: 2px 8px;
536
+ border-radius: 4px;
537
+ }
538
+
539
+ .error-msg {
540
+ color: #dc2626;
541
+ padding: 8px;
542
+ font-size: 0.9rem;
543
+ }
544
+ `,
545
+ ];
196
546
  __decorate([
197
547
  property({ type: String })
198
548
  ], KmapTermTree.prototype, "term", void 0);
199
549
  __decorate([
200
550
  property({ type: Number })
201
551
  ], KmapTermTree.prototype, "tension", void 0);
552
+ __decorate([
553
+ property({ type: Boolean })
554
+ ], KmapTermTree.prototype, "showLabels", void 0);
555
+ __decorate([
556
+ property({ type: Boolean })
557
+ ], KmapTermTree.prototype, "showDetails", void 0);
202
558
  __decorate([
203
559
  state()
204
- ], KmapTermTree.prototype, "tokens", void 0);
560
+ ], KmapTermTree.prototype, "ast", void 0);
205
561
  __decorate([
206
562
  state()
207
- ], KmapTermTree.prototype, "nodes", void 0);
563
+ ], KmapTermTree.prototype, "error", void 0);
208
564
  __decorate([
209
565
  state()
210
- ], KmapTermTree.prototype, "termNode", void 0);
566
+ ], KmapTermTree.prototype, "flatNodes", void 0);
211
567
  __decorate([
212
568
  state()
213
- ], KmapTermTree.prototype, "depths", void 0);
569
+ ], KmapTermTree.prototype, "edges", void 0);
214
570
  __decorate([
215
571
  state()
216
- ], KmapTermTree.prototype, "maxDepths", void 0);
572
+ ], KmapTermTree.prototype, "activeNodeId", void 0);
217
573
  __decorate([
218
574
  state()
219
- ], KmapTermTree.prototype, "connections", void 0);
575
+ ], KmapTermTree.prototype, "activeSubtreeIds", void 0);
220
576
  __decorate([
221
- query("kmap-term-tree-edges")
222
- ], KmapTermTree.prototype, "edgesElement", void 0);
577
+ state()
578
+ ], KmapTermTree.prototype, "treeHeight", void 0);
223
579
  __decorate([
224
- query("#tokens")
225
- ], KmapTermTree.prototype, "tokensElement", void 0);
226
- export class KmapTermTreeEdge extends LitElement {
227
- constructor() {
228
- super(...arguments);
229
- this.tension = .5;
230
- this.edges = [];
231
- }
232
- render() {
233
- const parent = this.offsetParent;
234
- //language=SVG
235
- return this.edges === undefined ? '' : svg `
236
- <svg style="position:absolute;left:0px;top:0px" width="${parent.clientWidth}" height="${parent.clientHeight}">
237
- ${this.edges.map(edge => svg `
238
- <path d="${this._path(edge)}" fill="none" stroke-width="2" stroke-opacity=".9"/>
239
- `)}
240
- </svg>
241
- `;
242
- }
243
- _path(edge) {
244
- let fromx = edge.from.x + edge.from.width / 2;
245
- let fromy = edge.from.y + edge.from.height / 2;
246
- let tox = edge.to.x + edge.to.width / 2;
247
- let toy = edge.to.y + edge.to.height / 2;
248
- var delta = (toy - fromy) * this.tension;
249
- var hx1 = fromx;
250
- var hy1 = fromy + delta;
251
- var hx2 = tox;
252
- var hy2 = toy - delta;
253
- return "M " + fromx + " " + fromy +
254
- " C " + hx1 + " " + hy1
255
- + " " + hx2 + " " + hy2
256
- + " " + tox + " " + toy;
257
- }
258
- }
259
- KmapTermTreeEdge.styles = css `
260
- :host {
261
- display: block;
262
- stroke: var(--kmap-term-tree-edge-color, #fbc02d);
263
- position: relative;
264
- }
265
- `;
580
+ state()
581
+ ], KmapTermTree.prototype, "highlightRect", void 0);
266
582
  __decorate([
267
- property({ type: Number })
268
- ], KmapTermTreeEdge.prototype, "tension", void 0);
583
+ query('#formula')
584
+ ], KmapTermTree.prototype, "formulaElement", void 0);
269
585
  __decorate([
270
- property({ type: Array })
271
- ], KmapTermTreeEdge.prototype, "edges", void 0);
272
- window.customElements.define('kmap-term-tree-edges', KmapTermTreeEdge);
586
+ query('.tree-canvas')
587
+ ], KmapTermTree.prototype, "treeCanvasElement", void 0);
273
588
  //# sourceMappingURL=KmapTermTree.js.map