yaml-flow 2.7.0 → 3.0.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.
- package/README.md +168 -3
- package/browser/ingest-board.js +296 -0
- package/browser/live-cards.js +303 -0
- package/browser/live-cards.schema.json +22 -2
- package/dist/card-compute/index.cjs +6751 -0
- package/dist/card-compute/index.cjs.map +1 -1
- package/dist/card-compute/index.d.cts +24 -1
- package/dist/card-compute/index.d.ts +24 -1
- package/dist/card-compute/index.js +6747 -1
- package/dist/card-compute/index.js.map +1 -1
- package/dist/{constants-BEbO2_OK.d.ts → constants-B_ftYTTE.d.ts} +36 -6
- package/dist/{constants-BNjeIlZ8.d.cts → constants-CiyHX8L-.d.cts} +36 -6
- package/dist/continuous-event-graph/index.cjs +399 -42
- package/dist/continuous-event-graph/index.cjs.map +1 -1
- package/dist/continuous-event-graph/index.d.cts +124 -5
- package/dist/continuous-event-graph/index.d.ts +124 -5
- package/dist/continuous-event-graph/index.js +396 -43
- package/dist/continuous-event-graph/index.js.map +1 -1
- package/dist/event-graph/index.cjs +6784 -44
- package/dist/event-graph/index.cjs.map +1 -1
- package/dist/event-graph/index.d.cts +5 -5
- package/dist/event-graph/index.d.ts +5 -5
- package/dist/event-graph/index.js +6777 -43
- package/dist/event-graph/index.js.map +1 -1
- package/dist/index.cjs +7678 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +7665 -73
- package/dist/index.js.map +1 -1
- package/dist/inference/index.cjs +17 -8
- package/dist/inference/index.cjs.map +1 -1
- package/dist/inference/index.d.cts +2 -2
- package/dist/inference/index.d.ts +2 -2
- package/dist/inference/index.js +17 -8
- package/dist/inference/index.js.map +1 -1
- package/dist/step-machine/index.cjs +6600 -0
- package/dist/step-machine/index.cjs.map +1 -1
- package/dist/step-machine/index.d.cts +26 -1
- package/dist/step-machine/index.d.ts +26 -1
- package/dist/step-machine/index.js +6596 -1
- package/dist/step-machine/index.js.map +1 -1
- package/dist/{types-DAI_a2as.d.ts → types-BpWrH1sf.d.cts} +16 -7
- package/dist/{types-DAI_a2as.d.cts → types-BpWrH1sf.d.ts} +16 -7
- package/dist/{types-mS_pPftm.d.ts → types-BuEo3wVG.d.ts} +1 -1
- package/dist/{types-C2lOwquM.d.cts → types-CxJg9Jrt.d.cts} +1 -1
- package/package.json +3 -2
- package/schema/event-graph.schema.json +254 -0
- package/schema/live-cards.schema.json +22 -2
package/browser/live-cards.js
CHANGED
|
@@ -51,10 +51,25 @@ var LiveCard = (function () {
|
|
|
51
51
|
.lc-todo-item:last-child { border-bottom:none; }
|
|
52
52
|
.lc-notes-preview { min-height:80px; }
|
|
53
53
|
.lc-source-pill { display:inline-flex; align-items:center; gap:0.5rem; padding:0.5rem 0.75rem; border-radius:2rem; font-size:0.8rem; background:var(--bs-light,#f8f9fa); border:1px solid var(--bs-border-color,#dee2e6); }
|
|
54
|
+
.lc-dropzone { border:2px dashed var(--bs-border-color,#dee2e6); border-radius:.5rem; padding:1.5rem; text-align:center; cursor:pointer; transition:border-color .15s,background .15s; }
|
|
55
|
+
.lc-dropzone:hover { border-color:var(--bs-primary,#0d6efd); }
|
|
56
|
+
.lc-dropzone.lc-drag-over { border-color:var(--bs-primary,#0d6efd); background:rgba(13,110,253,.05); }
|
|
57
|
+
.lc-dropzone.lc-disabled { pointer-events:none; opacity:.5; }
|
|
58
|
+
.lc-staged-file { display:flex; align-items:center; gap:.5rem; padding:.125rem 0; }
|
|
59
|
+
.lc-chat-el { display:flex; flex-direction:column; }
|
|
60
|
+
.lc-chat-body { flex:1; overflow-y:auto; max-height:300px; padding:.25rem; }
|
|
61
|
+
.lc-chat-bubble { padding:.375rem .625rem; margin:.25rem 0; border-radius:.75rem; max-width:85%; word-wrap:break-word; font-size:.875rem; }
|
|
62
|
+
.lc-chat-bubble-user { background:var(--bs-primary-bg-subtle,#cfe2ff); margin-left:auto; }
|
|
63
|
+
.lc-chat-bubble-assistant { background:var(--bs-light,#f8f9fa); }
|
|
64
|
+
.lc-chat-bubble-system { background:transparent; color:var(--bs-secondary,#6c757d); font-style:italic; text-align:center; max-width:100%; font-size:.8rem; }
|
|
65
|
+
.lc-chat-input-bar { display:flex; gap:.25rem; align-items:center; }
|
|
66
|
+
.lc-chat-processing { display:flex; align-items:center; gap:.5rem; padding:.25rem .5rem; color:var(--bs-secondary,#6c757d); font-size:.8rem; }
|
|
54
67
|
@media (max-width:576px) {
|
|
55
68
|
.lc-metric-value { font-size:1.5rem; }
|
|
56
69
|
.lc-chart-wrap { min-height:150px; }
|
|
57
70
|
.lc-chat-msg { max-width:95%; }
|
|
71
|
+
.lc-chat-body { max-height:200px; }
|
|
72
|
+
.lc-chat-bubble { max-width:95%; }
|
|
58
73
|
}
|
|
59
74
|
`;
|
|
60
75
|
document.head.appendChild(s);
|
|
@@ -150,6 +165,7 @@ var LiveCard = (function () {
|
|
|
150
165
|
markdown: config.markdown || null,
|
|
151
166
|
sanitize: config.sanitize || null,
|
|
152
167
|
chartLib: config.chartLib || null,
|
|
168
|
+
onAction: config.onAction || function () {},
|
|
153
169
|
};
|
|
154
170
|
|
|
155
171
|
const _cleanup = {}; // nodeId → { ac, timers, charts, unsubs }
|
|
@@ -671,6 +687,276 @@ var LiveCard = (function () {
|
|
|
671
687
|
el.innerHTML = `<pre class="small mb-0">${_esc(JSON.stringify(data, null, 2))}</pre>`;
|
|
672
688
|
}
|
|
673
689
|
|
|
690
|
+
// ---- file-upload ----
|
|
691
|
+
|
|
692
|
+
function _renderFileUpload(data, el, elemDef, node) {
|
|
693
|
+
const cleanup = _getCleanup(node.id);
|
|
694
|
+
const signal = cleanup.ac.signal;
|
|
695
|
+
const ed = elemDef.data || {};
|
|
696
|
+
const uploaded = Array.isArray(data) ? data : [];
|
|
697
|
+
const showUpload = ed.upload !== false;
|
|
698
|
+
const accept = ed.accept || ['.txt','.csv','.md','.json','.html','.xml','.pdf','.xlsx','.docx','.pptx','.png','.jpg','.jpeg'];
|
|
699
|
+
const acceptSet = new Set(accept.map(e => e.toLowerCase()));
|
|
700
|
+
const multiple = ed.multiple !== false;
|
|
701
|
+
const placeholder = ed.placeholder || 'Drop files here or click to browse';
|
|
702
|
+
const uid = 'lc-fu-' + (elemDef.id || Math.random().toString(36).slice(2, 8));
|
|
703
|
+
|
|
704
|
+
let stagedFiles = el._stagedFiles || [];
|
|
705
|
+
el._stagedFiles = stagedFiles;
|
|
706
|
+
|
|
707
|
+
let h = '';
|
|
708
|
+
|
|
709
|
+
// Drop zone
|
|
710
|
+
if (showUpload) {
|
|
711
|
+
h += `<div class="lc-dropzone mb-2" id="${uid}-dz">`;
|
|
712
|
+
h += '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="text-muted mb-1"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>';
|
|
713
|
+
h += `<div class="small text-muted">${_esc(placeholder)}</div>`;
|
|
714
|
+
h += `<input type="file" id="${uid}-fi" class="d-none"${multiple ? ' multiple' : ''} accept="${accept.join(',')}">`;
|
|
715
|
+
h += '</div>';
|
|
716
|
+
h += `<div id="${uid}-staged"></div>`;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Uploaded files list
|
|
720
|
+
if (uploaded.length) {
|
|
721
|
+
h += '<div class="lc-uploaded-files">';
|
|
722
|
+
uploaded.forEach(f => {
|
|
723
|
+
const name = typeof f === 'string' ? f : (f.name || '');
|
|
724
|
+
const url = typeof f === 'string' ? null : f.url;
|
|
725
|
+
h += '<div class="d-flex align-items-center gap-1 small mb-1">';
|
|
726
|
+
h += '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
727
|
+
if (url) h += `<a href="${_esc(url)}" class="text-truncate" target="_blank" download>${_esc(name)}</a>`;
|
|
728
|
+
else h += `<span class="text-truncate">${_esc(name)}</span>`;
|
|
729
|
+
h += '</div>';
|
|
730
|
+
});
|
|
731
|
+
h += '</div>';
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (!showUpload && !uploaded.length) {
|
|
735
|
+
h = `<p class="text-muted small">${_esc(ed.placeholder || 'No files')}</p>`;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
el.innerHTML = h;
|
|
739
|
+
|
|
740
|
+
if (!showUpload) {
|
|
741
|
+
el._fileUpload = { getFiles: () => [], clear: () => {} };
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const dz = document.getElementById(uid + '-dz');
|
|
746
|
+
const fi = document.getElementById(uid + '-fi');
|
|
747
|
+
const stagedEl = document.getElementById(uid + '-staged');
|
|
748
|
+
if (!dz) return;
|
|
749
|
+
|
|
750
|
+
function addFiles(fileList) {
|
|
751
|
+
for (const f of fileList) {
|
|
752
|
+
const ext = '.' + f.name.split('.').pop().toLowerCase();
|
|
753
|
+
if (!acceptSet.has(ext)) continue;
|
|
754
|
+
if (!stagedFiles.find(s => s.name === f.name)) stagedFiles.push(f);
|
|
755
|
+
}
|
|
756
|
+
renderStaged();
|
|
757
|
+
cfg.onPatchState(node.id, { _stagedFiles: stagedFiles.map(f => ({ name: f.name, size: f.size })) });
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function renderStaged() {
|
|
761
|
+
if (!stagedFiles.length) { stagedEl.innerHTML = ''; return; }
|
|
762
|
+
let sh = '';
|
|
763
|
+
stagedFiles.forEach((f, i) => {
|
|
764
|
+
sh += '<div class="lc-staged-file">';
|
|
765
|
+
sh += '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
766
|
+
sh += `<span class="small flex-grow-1 text-truncate">${_esc(f.name)}</span>`;
|
|
767
|
+
sh += `<button class="btn btn-sm btn-link text-danger p-0 lc-rm-staged" data-idx="${i}">×</button>`;
|
|
768
|
+
sh += '</div>';
|
|
769
|
+
});
|
|
770
|
+
stagedEl.innerHTML = sh;
|
|
771
|
+
stagedEl.querySelectorAll('.lc-rm-staged').forEach(btn => {
|
|
772
|
+
btn.addEventListener('click', () => {
|
|
773
|
+
stagedFiles.splice(parseInt(btn.dataset.idx), 1);
|
|
774
|
+
el._stagedFiles = stagedFiles;
|
|
775
|
+
renderStaged();
|
|
776
|
+
}, { signal });
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
dz.addEventListener('click', () => fi.click(), { signal });
|
|
781
|
+
dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('lc-drag-over'); }, { signal });
|
|
782
|
+
dz.addEventListener('dragleave', () => dz.classList.remove('lc-drag-over'), { signal });
|
|
783
|
+
dz.addEventListener('drop', e => { e.preventDefault(); dz.classList.remove('lc-drag-over'); addFiles(e.dataTransfer.files); }, { signal });
|
|
784
|
+
fi.addEventListener('change', e => { addFiles(e.target.files); e.target.value = ''; }, { signal });
|
|
785
|
+
|
|
786
|
+
renderStaged();
|
|
787
|
+
|
|
788
|
+
el._fileUpload = {
|
|
789
|
+
getFiles: () => stagedFiles,
|
|
790
|
+
clear: () => { stagedFiles = []; el._stagedFiles = []; renderStaged(); },
|
|
791
|
+
disable: () => { dz.classList.add('lc-disabled'); fi.disabled = true; },
|
|
792
|
+
enable: () => { dz.classList.remove('lc-disabled'); fi.disabled = false; },
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// ---- chat (element kind) ----
|
|
797
|
+
|
|
798
|
+
function _renderChatEl(data, el, elemDef, node) {
|
|
799
|
+
const cleanup = _getCleanup(node.id);
|
|
800
|
+
const signal = cleanup.ac.signal;
|
|
801
|
+
const ed = elemDef.data || {};
|
|
802
|
+
const messages = Array.isArray(data) ? data : [];
|
|
803
|
+
const placeholder = ed.placeholder || 'Type a message...';
|
|
804
|
+
const canAttach = ed.fileAttach === true;
|
|
805
|
+
const accept = ed.fileAccept || ['.txt','.csv','.md','.json','.html','.xml','.pdf','.xlsx','.docx','.pptx','.png','.jpg','.jpeg'];
|
|
806
|
+
const uid = 'lc-ch-' + (elemDef.id || Math.random().toString(36).slice(2, 8));
|
|
807
|
+
|
|
808
|
+
let h = '<div class="lc-chat-el">';
|
|
809
|
+
h += `<div class="lc-chat-body" id="${uid}-body"></div>`;
|
|
810
|
+
h += '<div class="lc-chat-input-bar">';
|
|
811
|
+
if (canAttach) {
|
|
812
|
+
h += `<input type="file" id="${uid}-fi" class="d-none" multiple accept="${accept.join(',')}">`;
|
|
813
|
+
h += `<button class="btn btn-sm btn-outline-secondary" id="${uid}-attach" title="Attach files" type="button">`;
|
|
814
|
+
h += '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>';
|
|
815
|
+
h += '</button>';
|
|
816
|
+
}
|
|
817
|
+
h += `<input type="text" class="form-control form-control-sm flex-grow-1" id="${uid}-input" placeholder="${_esc(placeholder)}">`;
|
|
818
|
+
h += `<button class="btn btn-sm btn-outline-primary" id="${uid}-send" type="button">`;
|
|
819
|
+
h += '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>';
|
|
820
|
+
h += '</button></div>';
|
|
821
|
+
if (canAttach) h += `<div id="${uid}-staged" class="mt-1"></div>`;
|
|
822
|
+
h += '</div>';
|
|
823
|
+
|
|
824
|
+
el.innerHTML = h;
|
|
825
|
+
|
|
826
|
+
const body = document.getElementById(uid + '-body');
|
|
827
|
+
const input = document.getElementById(uid + '-input');
|
|
828
|
+
const sendBtn = document.getElementById(uid + '-send');
|
|
829
|
+
const attachBtn = canAttach ? document.getElementById(uid + '-attach') : null;
|
|
830
|
+
const fileInput = canAttach ? document.getElementById(uid + '-fi') : null;
|
|
831
|
+
const stagedEl = canAttach ? document.getElementById(uid + '-staged') : null;
|
|
832
|
+
|
|
833
|
+
let stagedFiles = [];
|
|
834
|
+
|
|
835
|
+
function appendMsg(msg) {
|
|
836
|
+
const bub = document.createElement('div');
|
|
837
|
+
const roleClass = msg.role === 'user' ? 'lc-chat-bubble-user'
|
|
838
|
+
: msg.role === 'assistant' ? 'lc-chat-bubble-assistant'
|
|
839
|
+
: 'lc-chat-bubble-system';
|
|
840
|
+
bub.className = 'lc-chat-bubble ' + roleClass;
|
|
841
|
+
if (msg.role === 'assistant') {
|
|
842
|
+
bub.innerHTML = _renderMd(msg.text || '');
|
|
843
|
+
} else {
|
|
844
|
+
bub.textContent = msg.text || '';
|
|
845
|
+
}
|
|
846
|
+
if (msg.files && msg.files.length) {
|
|
847
|
+
const fDiv = document.createElement('div');
|
|
848
|
+
fDiv.className = 'small mt-1';
|
|
849
|
+
msg.files.forEach(f => {
|
|
850
|
+
const name = typeof f === 'string' ? f : f.name;
|
|
851
|
+
fDiv.innerHTML += '\uD83D\uDCCE ' + _esc(name) + '<br>';
|
|
852
|
+
});
|
|
853
|
+
bub.appendChild(fDiv);
|
|
854
|
+
}
|
|
855
|
+
body.appendChild(bub);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
messages.forEach(appendMsg);
|
|
859
|
+
body.scrollTop = body.scrollHeight;
|
|
860
|
+
|
|
861
|
+
function renderStaged() {
|
|
862
|
+
if (!stagedEl) return;
|
|
863
|
+
if (!stagedFiles.length) { stagedEl.innerHTML = ''; return; }
|
|
864
|
+
stagedEl.innerHTML = stagedFiles.map((f, i) =>
|
|
865
|
+
`<div class="d-flex align-items-center gap-1 small"><span>\uD83D\uDCCE ${_esc(f.name)}</span><button class="btn btn-sm btn-link text-danger p-0 lc-rm-cs" data-idx="${i}">×</button></div>`
|
|
866
|
+
).join('');
|
|
867
|
+
stagedEl.querySelectorAll('.lc-rm-cs').forEach(btn => {
|
|
868
|
+
btn.addEventListener('click', () => { stagedFiles.splice(parseInt(btn.dataset.idx), 1); renderStaged(); }, { signal });
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (attachBtn && fileInput) {
|
|
873
|
+
const acceptS = new Set(accept.map(x => x.toLowerCase()));
|
|
874
|
+
attachBtn.addEventListener('click', () => fileInput.click(), { signal });
|
|
875
|
+
fileInput.addEventListener('change', e => {
|
|
876
|
+
for (const f of e.target.files) {
|
|
877
|
+
const ext = '.' + f.name.split('.').pop().toLowerCase();
|
|
878
|
+
if (acceptS.has(ext) && !stagedFiles.find(s => s.name === f.name)) stagedFiles.push(f);
|
|
879
|
+
}
|
|
880
|
+
e.target.value = '';
|
|
881
|
+
renderStaged();
|
|
882
|
+
}, { signal });
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function doSend() {
|
|
886
|
+
const text = input.value.trim();
|
|
887
|
+
if (!text && !stagedFiles.length) return;
|
|
888
|
+
const msg = { role: 'user', text: text || '' };
|
|
889
|
+
if (stagedFiles.length) msg.files = stagedFiles.map(f => ({ name: f.name, size: f.size }));
|
|
890
|
+
appendMsg(msg);
|
|
891
|
+
body.scrollTop = body.scrollHeight;
|
|
892
|
+
input.value = '';
|
|
893
|
+
const filesToSend = stagedFiles.slice();
|
|
894
|
+
stagedFiles = [];
|
|
895
|
+
renderStaged();
|
|
896
|
+
cfg.onAction(node.id, 'chat-send', { text: msg.text, files: filesToSend, elemId: elemDef.id });
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
sendBtn.addEventListener('click', doSend, { signal });
|
|
900
|
+
input.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); doSend(); } }, { signal });
|
|
901
|
+
|
|
902
|
+
el._chat = {
|
|
903
|
+
appendMessage: (role, text, files) => { appendMsg({ role, text, files }); body.scrollTop = body.scrollHeight; },
|
|
904
|
+
showProcessing: (text) => {
|
|
905
|
+
let ind = body.querySelector('.lc-chat-processing');
|
|
906
|
+
if (!ind) {
|
|
907
|
+
ind = document.createElement('div');
|
|
908
|
+
ind.className = 'lc-chat-processing';
|
|
909
|
+
ind.innerHTML = '<span class="spinner-border spinner-border-sm"></span><span class="small">Processing...</span>';
|
|
910
|
+
body.appendChild(ind);
|
|
911
|
+
}
|
|
912
|
+
if (text) ind.querySelector('.small').textContent = text;
|
|
913
|
+
body.scrollTop = body.scrollHeight;
|
|
914
|
+
},
|
|
915
|
+
removeProcessing: () => { const ind = body.querySelector('.lc-chat-processing'); if (ind) ind.remove(); },
|
|
916
|
+
disable: () => { input.disabled = true; sendBtn.disabled = true; if (attachBtn) attachBtn.disabled = true; },
|
|
917
|
+
enable: () => { input.disabled = false; sendBtn.disabled = false; if (attachBtn) attachBtn.disabled = false; },
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// ---- actions ----
|
|
922
|
+
|
|
923
|
+
function _renderActions(data, el, elemDef, node) {
|
|
924
|
+
const cleanup = _getCleanup(node.id);
|
|
925
|
+
const signal = cleanup.ac.signal;
|
|
926
|
+
const ed = elemDef.data || {};
|
|
927
|
+
const buttons = ed.buttons || (Array.isArray(data) ? data : []);
|
|
928
|
+
if (!buttons.length) { el.innerHTML = ''; return; }
|
|
929
|
+
|
|
930
|
+
let h = '<div class="d-flex gap-2 flex-wrap">';
|
|
931
|
+
buttons.forEach(btn => {
|
|
932
|
+
const style = btn.style || 'outline-secondary';
|
|
933
|
+
const size = btn.size || 'sm';
|
|
934
|
+
const dis = typeof btn.disabled === 'string' ? _resolveBind(node, btn.disabled) : btn.disabled;
|
|
935
|
+
h += `<button class="btn btn-${_esc(style)} btn-${size}" data-action-id="${_esc(btn.id)}"${dis ? ' disabled' : ''}>`;
|
|
936
|
+
h += _esc(btn.label || btn.id);
|
|
937
|
+
h += '</button>';
|
|
938
|
+
});
|
|
939
|
+
h += '</div>';
|
|
940
|
+
el.innerHTML = h;
|
|
941
|
+
|
|
942
|
+
el.querySelectorAll('[data-action-id]').forEach(btnEl => {
|
|
943
|
+
btnEl.addEventListener('click', () => {
|
|
944
|
+
cfg.onAction(node.id, 'action', { buttonId: btnEl.dataset.actionId, elemId: elemDef.id });
|
|
945
|
+
}, { signal });
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
el._actions = {
|
|
949
|
+
setDisabled: (buttonId, disabled) => {
|
|
950
|
+
const b = el.querySelector(`[data-action-id="${buttonId}"]`);
|
|
951
|
+
if (b) b.disabled = disabled;
|
|
952
|
+
},
|
|
953
|
+
setLabel: (buttonId, label) => {
|
|
954
|
+
const b = el.querySelector(`[data-action-id="${buttonId}"]`);
|
|
955
|
+
if (b) b.textContent = label;
|
|
956
|
+
},
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
674
960
|
// ---- Register built-in renderers ----
|
|
675
961
|
|
|
676
962
|
_renderers.table = _renderTable;
|
|
@@ -687,6 +973,9 @@ var LiveCard = (function () {
|
|
|
687
973
|
_renderers.text = _renderText;
|
|
688
974
|
_renderers.markdown = _renderMarkdown;
|
|
689
975
|
_renderers.custom = _renderCustom;
|
|
976
|
+
_renderers['file-upload'] = _renderFileUpload;
|
|
977
|
+
_renderers['chat'] = _renderChatEl;
|
|
978
|
+
_renderers.actions = _renderActions;
|
|
690
979
|
|
|
691
980
|
// ===========================================================================
|
|
692
981
|
// _renderElements — render all view.elements for a card node
|
|
@@ -696,6 +985,8 @@ var LiveCard = (function () {
|
|
|
696
985
|
const view = node.view;
|
|
697
986
|
if (!view || !Array.isArray(view.elements)) { containerEl.innerHTML = ''; return; }
|
|
698
987
|
|
|
988
|
+
if (_nodeEls[node.id]) _nodeEls[node.id].elements = {};
|
|
989
|
+
|
|
699
990
|
const container = document.createElement('div');
|
|
700
991
|
container.className = 'row g-2';
|
|
701
992
|
|
|
@@ -729,6 +1020,8 @@ var LiveCard = (function () {
|
|
|
729
1020
|
inner.innerHTML = `<div class="text-danger small">Render error: ${_esc(e.message)}</div>`;
|
|
730
1021
|
}
|
|
731
1022
|
|
|
1023
|
+
if (elemDef.id && _nodeEls[node.id]) _nodeEls[node.id].elements[elemDef.id] = inner;
|
|
1024
|
+
|
|
732
1025
|
container.appendChild(col);
|
|
733
1026
|
});
|
|
734
1027
|
|
|
@@ -929,6 +1222,15 @@ var LiveCard = (function () {
|
|
|
929
1222
|
chatEl.scrollTop = chatEl.scrollHeight;
|
|
930
1223
|
}
|
|
931
1224
|
|
|
1225
|
+
// ===========================================================================
|
|
1226
|
+
// Element access
|
|
1227
|
+
// ===========================================================================
|
|
1228
|
+
|
|
1229
|
+
function getElement(nodeId, elemId) {
|
|
1230
|
+
const info = _nodeEls[nodeId];
|
|
1231
|
+
return (info && info.elements && info.elements[elemId]) || null;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
932
1234
|
// ===========================================================================
|
|
933
1235
|
// Return engine
|
|
934
1236
|
// ===========================================================================
|
|
@@ -941,6 +1243,7 @@ var LiveCard = (function () {
|
|
|
941
1243
|
notify,
|
|
942
1244
|
subscribe,
|
|
943
1245
|
appendChatMessage,
|
|
1246
|
+
getElement,
|
|
944
1247
|
registerRenderer(name, fn) { _renderers[name] = fn; },
|
|
945
1248
|
renderers: _renderers,
|
|
946
1249
|
};
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"kind": {
|
|
117
117
|
"enum": ["metric", "table", "chart", "form", "filter", "list",
|
|
118
118
|
"notes", "todo", "alert", "narrative", "badge", "text",
|
|
119
|
-
"markdown", "custom"]
|
|
119
|
+
"markdown", "custom", "file-upload", "chat", "actions"]
|
|
120
120
|
},
|
|
121
121
|
"label": { "type": "string", "description": "Heading above this element" },
|
|
122
122
|
"className": { "type": "string", "description": "Bootstrap grid class, e.g. 'col-12 col-md-6'" },
|
|
@@ -141,7 +141,27 @@
|
|
|
141
141
|
}
|
|
142
142
|
},
|
|
143
143
|
"colorMap": { "type": "object", "description": "badge: value → Bootstrap color" },
|
|
144
|
-
"style": { "enum": ["heading", "muted", "default"], "description": "text: display style" }
|
|
144
|
+
"style": { "enum": ["heading", "muted", "default"], "description": "text: display style" },
|
|
145
|
+
"upload": { "type": "boolean", "default": true, "description": "file-upload: show drop zone (false = read-only file list)" },
|
|
146
|
+
"accept": { "type": "array", "items": { "type": "string" }, "description": "file-upload: allowed extensions" },
|
|
147
|
+
"multiple": { "type": "boolean", "default": true, "description": "file-upload: allow multiple files" },
|
|
148
|
+
"fileAttach": { "type": "boolean", "default": false, "description": "chat: enable inline file attachments" },
|
|
149
|
+
"fileAccept": { "type": "array", "items": { "type": "string" }, "description": "chat: allowed attachment extensions" },
|
|
150
|
+
"buttons": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"description": "actions: button definitions",
|
|
153
|
+
"items": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"required": ["id", "label"],
|
|
156
|
+
"properties": {
|
|
157
|
+
"id": { "type": "string" },
|
|
158
|
+
"label": { "type": "string" },
|
|
159
|
+
"style": { "type": "string", "description": "Bootstrap button variant, e.g. 'success', 'outline-danger'" },
|
|
160
|
+
"size": { "type": "string", "default": "sm" },
|
|
161
|
+
"disabled": { "oneOf": [{ "type": "boolean" }, { "type": "string", "description": "state path — truthy = disabled" }] }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
145
165
|
}
|
|
146
166
|
}
|
|
147
167
|
}
|