1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
function renderTalkTree(data) {
var container = document.getElementById('talktree');
if (!container) return;
if (!data || !data.talk || !data.talk.nodes) {
container.innerHTML = '<p style="color:#888">No conversation tree defined.</p>';
return;
}
window._talkData = data;
var nodes = data.talk.nodes;
var html = '<div class="talk-tree">';
html += renderTreeNodes(nodes, 'start', {});
html += '</div>';
container.innerHTML = html;
}
function renderTreeNodes(nodes, rootKey, seen) {
var node = nodes[rootKey];
if (!node) return '';
if (seen[rootKey]) {
return '<div class="node ref"><span class="key">' + esc(rootKey) + '</span> <span class="preview">(already shown)</span></div>';
}
seen[rootKey] = true;
var html = '';
html += '<div class="node root' + (rootKey === 'start' ? ' open' : '') + '">';
html += '<div class="node-header" onclick="this.parentElement.classList.toggle(\'open\')">';
html += '<span class="arrow"></span>';
html += '<span class="key">' + esc(rootKey) + '</span>';
html += '<span class="preview">' + esc((node.message || '').substring(0, 60)) + '</span>';
html += '</div>';
html += '<div class="node-children">';
html += '<div style="margin-left:8px;font-size:11px">';
if (node.message) html += '<div><b>Message:</b> ' + esc(node.message) + '</div>';
if (node.condition) html += '<div><b>Condition:</b> ' + esc(JSON.stringify(node.condition)) + '</div>';
if (node.goto) html += '<div><b>Goto:</b> ' + esc(node.goto) + '</div>';
html += '</div>';
html += '<div style="margin-left:10px"><b style="font-size:11px;color:#aaa">Options:</b>';
if (node.options && node.options.length > 0) {
node.options.forEach(function(opt, idx) {
html += '<div class="option">';
html += '<span>' + esc(opt.text || '') + '</span> \u2192 <span style="color:#8cf">' + esc(opt.goto || '') + '</span>';
if (opt.condition) html += ' <span style="color:#888;font-size:10px">[cond]</span>';
if (opt.action) html += ' <span style="color:#888;font-size:10px">[action]</span>';
html += ' <button class="btn btn-danger btn-sm" onclick="deleteOption(event,\'' + escAttr(rootKey) + '\',' + idx + ')">Delete Option</button>';
html += '</div>';
});
} else {
html += '<div class="option" style="color:#888">No options</div>';
}
html += '<div style="margin-top:4px"><button class="btn btn-primary btn-sm" onclick="addOption(event,\'' + escAttr(rootKey) + '\')">+ Add Option</button></div>';
html += '</div>';
html += '</div></div>';
if (node.options) {
node.options.forEach(function(opt) {
if (opt.goto && nodes[opt.goto]) {
html += renderTreeNodes(nodes, opt.goto, seen);
}
});
}
return html;
}
function addOption(e, nodeKey) {
if (e) e.stopPropagation();
var data = window._talkData;
if (!data || !data.talk || !data.talk.nodes || !data.talk.nodes[nodeKey]) return;
var text = prompt('Option text:');
if (text === null) return;
var dest = prompt('Goto node key:') || '';
var node = data.talk.nodes[nodeKey];
if (!node.options) node.options = [];
node.options.push({ text: text, goto: dest });
renderTalkTree(data);
if (typeof window.onTalkTreeChange === 'function') window.onTalkTreeChange(data);
}
function deleteOption(e, nodeKey, idx) {
if (e) e.stopPropagation();
var data = window._talkData;
if (!data || !data.talk || !data.talk.nodes || !data.talk.nodes[nodeKey]) return;
var node = data.talk.nodes[nodeKey];
if (!node.options || idx < 0 || idx >= node.options.length) return;
if (!confirm('Delete this option?')) return;
node.options.splice(idx, 1);
renderTalkTree(data);
if (typeof window.onTalkTreeChange === 'function') window.onTalkTreeChange(data);
}
|