-
-
Drag and drop video file
-
-
No video chosen
-
-
![]()
+
+
+
Drag and drop video file
+
+
No video chosen
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
---Starting---
+
+
+
---Transkribing---
+
+
+
---Document creation---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
diff --git a/electron/main/preload.js b/electron/main/preload.js
index b027cde..26aa314 100644
--- a/electron/main/preload.js
+++ b/electron/main/preload.js
@@ -15,12 +15,22 @@ try {
getModuleNames: () => ipcRenderer.invoke('get-module-names')
})
-
-
- ipcRenderer.on("progress", (event, resp) => {
- alert(`Finished step ${resp.curstep} of ${resp.totalsteps}`)
+ contextBridge.exposeInMainWorld('electron', {
+ progress: (callback) => ipcRenderer.on('progress', callback)
})
+ contextBridge.exposeInMainWorld('audios', {
+ speakerAudios: (callback) => ipcRenderer.on('speakerAudios', callback)
+ })
+ contextBridge.exposeInMainWorld("submitSpeaker", {
+ submitSpeaker: (speaker_names) => {ipcRenderer.send("speaker_submit", speaker_names)}
+ })
+
+ contextBridge.exposeInMainWorld("download", {
+ file_download: () => {ipcRenderer.send("file_download")}
+ })
+
+
ipcRenderer.on("error", (event, err) => {alert(err)})
} catch (error) {
console.log("Error in preload.js");
diff --git a/electron/main/renderer.js b/electron/main/renderer.js
index fcdd894..f972d78 100644
--- a/electron/main/renderer.js
+++ b/electron/main/renderer.js
@@ -33,7 +33,6 @@ uploadContainer.addEventListener("drop", (e) => {
window.addEventListener('load', async (e) => {
try {
- console.log("test");
loadLanguageOptions();
const value = await window.onStartup.getModuleNames();
loadAiOptions(value.ai_modules);
@@ -55,24 +54,16 @@ language_option.addEventListener('change', (e)=>{
});
+//listener for the file explorer search when something got selected
videoUpload.addEventListener("change", () => {
try {
- activateSubmitBtn(videoUpload.files.length > 0);
+ if (videoUpload.files.length > 0) {
+ const file = videoUpload.files;
+ handleFiles(file);
+ }
} catch (error) {
console.log(error);
}
-
-});
-
-//listener for the file explorer search when something got selected
-videoUpload.addEventListener('change', () => {
- try {
- handleFiles(videoUpload.files);
- } catch (error) {
- console.log("Error in manualBtn EventListener change");
- console.log(error);
- }
-
});
//listener for the file explorer search
@@ -84,4 +75,169 @@ manualUploadBtn.addEventListener('click', () => {
console.log(error);
}
-});
\ No newline at end of file
+});
+
+stepButtons.forEach(btn => {
+ btn.addEventListener("click", () => {
+ try {
+ const step = parseInt(btn.dataset.step);
+ showStep(step);
+ } catch (error) {
+
+ }
+ });
+});
+
+prevBtn.addEventListener("click", () => {
+ try {
+ if (currentStep > 1) showStep(currentStep - 1);
+ } catch (error) {
+
+ }
+});
+
+nextBtn.addEventListener("click", () => {
+ try {
+ if(currentStep < totalSteps) showStep(currentStep + 1);
+ } catch (error) {
+
+ }
+});
+
+//Checkboxlistener so that only one can be selected at a time
+docFormat.addEventListener("change", (e) =>{
+ try {
+ if(docFormat.checked){
+ docFormatSummary1.checked = false;
+ docFormatSummary2.checked = false;
+ docFormatSummary3.checked = false;
+ docFormatCustom.checked = false;
+ }
+ } catch (error) {
+
+ }
+});
+docFormatSummary1.addEventListener("change", (e) =>{
+ try {
+ if(docFormatSummary1.checked){
+ docFormat.checked = false;
+ docFormatSummary2.checked = false;
+ docFormatSummary3.checked = false;
+ docFormatCustom.checked = false;
+ }
+ } catch (error) {
+
+ }
+});
+docFormatSummary2.addEventListener("change", (e) =>{
+ try {
+ if(docFormatSummary2.checked){
+ docFormatSummary1.checked = false;
+ docFormat.checked = false;
+ docFormatSummary3.checked = false;
+ docFormatCustom.checked = false;
+ }
+ } catch (error) {
+
+ }
+});
+docFormatSummary3.addEventListener("change", (e) =>{
+ try {
+ if(docFormatSummary3.checked){
+ docFormatSummary1.checked = false;
+ docFormatSummary2.checked = false;
+ docFormat.checked = false;
+ docFormatCustom.checked = false;
+ }
+ } catch (error) {
+
+ }
+});
+docFormatCustom.addEventListener("change", (e) =>{
+ try {
+ if(docFormatCustom.checked){
+ docFormatSummary1.checked = false;
+ docFormatSummary2.checked = false;
+ docFormatSummary3.checked = false;
+ docFormat.checked = false;
+ }
+ } catch (error) {
+
+ }
+});
+
+//Speaker change listener
+cur_speaker.addEventListener("change", (e) =>{
+ try {
+ document.getElementById("speakerAudioViewer").src = valy[e.target.value].source;
+ } catch (error) {
+
+ }
+});
+
+window.audios.speakerAudios((event, arg) => {
+ setSpeakerAudiosValue(arg);
+ loadSpeakerOptions(arg);
+});
+
+window.electron.progress((event, arg) => {
+ if(arg.curstep == 1){
+ setCircleOne();
+ }else if(arg.curstep == 2){
+ setCircleZwo();
+ } else if(arg.curstep == 3){
+ setCircleThree();
+ }else if(arg.curstep == 4){
+ setCircleFour();
+ }
+});
+
+function setCircleOne(){
+ try {
+ if(document.getElementById("box1").style.backgroundColor == "green"){
+ document.getElementById("box1").style.backgroundColor = "red";
+ }else{
+ document.getElementById("box1").style.backgroundColor = "green";
+ }
+ } catch (error) {
+ }
+
+};
+function setCircleZwo(){
+ try {
+ if(document.getElementById("box2").style.backgroundColor == "green"){
+ document.getElementById("box2").style.backgroundColor = "red";
+ }else{
+ document.getElementById("box2").style.backgroundColor = "green";
+ }
+ } catch (error) {
+
+ }
+
+};
+function setCircleThree(){
+ try {
+ if(document.getElementById("box3").style.backgroundColor == "green"){
+ document.getElementById("box3").style.backgroundColor = "red";
+ }else{
+ document.getElementById("box3").style.backgroundColor = "green";
+ }
+ } catch (error) {
+
+ }
+
+};
+function setCircleFour(){
+ try {
+ if(document.getElementById("box4").style.backgroundColor == "green"){
+ document.getElementById("box4").style.backgroundColor = "red";
+ }else{
+ document.getElementById("box4").style.backgroundColor = "green";
+ }
+ } catch (error) {
+
+ }
+};
+
+
+
diff --git a/electron/main/script.js b/electron/main/script.js
index 51935ed..6500dbc 100644
--- a/electron/main/script.js
+++ b/electron/main/script.js
@@ -1,4 +1,4 @@
-
+let currentVideoPath = null;
//function to check if one checkbox is at least klicked
function checkBoxes() {
try {
@@ -25,7 +25,6 @@ function checkBoxes() {
var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]);
var pathToLower = pathTest.toLowerCase();
if(testEndings.some(e => pathToLower.endsWith(e))){
- document.getElementById("progressbar").style.visibility = "visible";
//assembly of the json for the main
const selectedStyles = [checkedCounter];
@@ -37,6 +36,11 @@ function checkBoxes() {
iter++;
}
});
+ document.getElementById("testy").style.visibility = "visible"
+ document.getElementById("box1").style.backgroundColor = "red";
+ document.getElementById("box2").style.backgroundColor = "red";
+ document.getElementById("box3").style.backgroundColor = "red";
+ document.getElementById("box4").style.backgroundColor = "red";
console.log(selectedCheckboxes);
const outputType = document.getElementById("output_type");
const transcriptionType = document.getElementById("transkript_type");
@@ -44,15 +48,15 @@ function checkBoxes() {
const sendingPackage = {
"video": {
"module":"extraction-video-to-audio",
- "inputVideoPath": pathTest,
- "outputType": outputType.value
+ "inputVideoPath": pathTest
},
"transcription": {
"module": transcriptionType.value
},
"document": {
"module":aiType.value,
- "styles": selectedStyles
+ "styles": selectedStyles,
+ "outputType": outputType.value
}
};
window.submit.submit(sendingPackage)
@@ -105,6 +109,7 @@ function handleFiles(files) {
const filePath = window.explorer.onFileDrop(files[0])
videoUpload.files = files;
fileName.textContent = `Chosen video: ${file.name}`;
+ currentVideoPath = filePath;
generateThumbnail(filePath);
activateSubmitBtn(true);
}
@@ -167,6 +172,25 @@ function loadTranscriptionOptions(options){
}
}
+//function to load data type options to the drop down list
+function loadDataTypeOptions(options){
+ try {
+ var menu = document.getElementById('output_type');
+ var object_holdy;
+ var choice ;
+ object_holdy = options
+ for(i = 0; i < options.length; i++){
+ choice = document.createElement('option');
+ choice.textContent = object_holdy[i].displayname;
+ choice.value = object_holdy[i].name;
+ menu.appendChild(choice);
+ }
+ } catch (error) {
+ console.log("Error in script.js function loadDataTypeOptions");
+ console.log(error);
+ }
+}
+
//function to load language options to the drop down list
function loadLanguageOptions(){
try {
@@ -180,16 +204,48 @@ function loadLanguageOptions(){
choice.value = object_holdy[i];
menu.appendChild(choice);
}
-
} catch (error) {
console.log("Error in script.js loadLanguageOptions function");
console.log(error);
}
}
+//function to load speaker options to the drop down list
+function loadSpeakerOptions(options){
+ try {
+ var menu = document.getElementById('speaker_option');
+ var object_holdy;
+ var choice;
+ object_holdy = options.keys();
+ for(i = 0; i < options.length; i++){
+ choice = document.createElement('option');
+ choice.textContent = options[object_holdy[i]].name;
+ choice.value = options[object_holdy[i]].name;
+ menu.appendChild(choice);
+ }
+ } catch (error) {
+ console.log("Error in script.js loadSpeakerOptions function");
+ console.log(error);
+ }
+}
+
+//function to load speaker audio file options to the drop down list
+function loadSpeakerAudio(option){
+ try {
+ var menu = document.getElementById('speakerAudioViewer');
+ var aud = document.createElement("source");
+ aud.src = options;
+ menu.appendChild(aud);
+ } catch (error) {
+ console.log("Error in script.js loadSpeakerAudio function");
+ console.log(error);
+ }
+}
+
+
function activateSubmitBtn(hasFile){
try {
- submitButton.disabled = !hasFile;
+ document.getElementById("submitButton").disabled = !hasFile;
} catch (error) {
console.log(error);
}
@@ -214,3 +270,68 @@ function generateThumbnail(path){
videoElement.style.maxHeight = 40;
videoElement.autoplay = false;
}
+
+//Step-navigation
+const steps = document.querySelectorAll(".step");
+const stepButtons = document.querySelectorAll(".step-item");
+let currentStep = 1;
+const totalSteps = steps.length;
+
+function showStep(stepNumber) {
+ if (stepNumber < 1 || stepNumber > totalSteps){
+ console.error("StepNumber out of Bounds", stepNumber);
+ return;
+ }
+ steps.forEach(step => step.style.display = "none");
+ document.getElementById("step" + stepNumber).style.display = "block";
+
+ stepButtons.forEach(btn => btn.classList.remove("active"));
+ document.querySelector(`.step-item[data-step="${stepNumber}"]`).classList.add("active");
+ const activeBtn = document.querySelector(`.step-item[data-step="${stepNumber}"]`);
+
+ if(activeBtn) activeBtn.classList.add("active");
+
+ prevBtn.disabled = stepNumber == 1;
+ nextBtn.disabled = stepNumber === totalSteps;
+ currentStep = stepNumber;
+}
+
+
+//Audio value setter
+var speakerAudios = {};
+var speakerEndValues = {};
+function setSpeakerAudiosValue(valy){
+ try {
+ speakerAudios = valy;
+ speakerRewriten = valy;
+ document.getElementById("speakerAudioViewer").src = valy.speakerA.source;
+ } catch (error) {
+
+ }
+}
+
+function rewriteSpeakerName(){
+ try {
+ var tempy = document.getElementById("cur_speaker").textContent;
+ speakerAudios[tempy].name = document.getElementById("newSpeaker").textContent;
+ document.getElementById("cur_speaker").textContent = document.getElementById("newSpeaker").textContent;
+ } catch (error) {
+
+ }
+}
+
+function sendSpeakerPackages(){
+ try {
+ window.sendSpeakerPackages(speakerAudios);
+ } catch (error) {
+
+ }
+}
+
+function fileDownload() {
+ try {
+ window.download.file_download();
+ } catch (error) {
+ console.error("Download failed:", error);
+ }
+}
diff --git a/electron/main/style.css b/electron/main/style.css
index 0912516..cc4eed9 100644
--- a/electron/main/style.css
+++ b/electron/main/style.css
@@ -6,11 +6,33 @@ body {
align-items: center;
height: 100vh;
background-color: #f2f3f4;
- gap: 15px;
+ gap: 10px;
margin: 0;
}
-
+#h1 {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ margin: 0;
+ z-index: 20;
+
+}
+
+#h1-wrapper {
+ position: relative;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ width: 770px;
+ height: 60px;
+ background-color: #FFF;
+ box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
+ border-radius: 5px;
+ margin-bottom: 10px;
+ display: flex;
+ align-items: center;
+}
+
.upload-container {
background: white;
padding: 40px;
@@ -91,25 +113,73 @@ input[type="file"] {
display: none;
}
-.checkbox-container{
-margin-top: 8px;
-display: flex;
-align-items: center;
-gap: 5px;
-}
-
.checkbox-group {
- margin-top: 15px;
- margin-bottom: 15px;
+ --borderColor: #007bfff5;
+ --borderWidth: .125em;
+ }
+
+ .checkbox-group input[type=checkbox] {
+ -webkit-appearance: none;
+ appearance: none;
+ vertical-align: middle;
+ background: #fff;
+ font-size: 1.8em;
+ border-radius: 0.125em;
+ display: inline-block;
+ border: var(--borderWidth) solid var(--borderColor);
+ width: 1em;
+ height: 1em;
+ position: relative;
+ }
+ .checkbox-group input[type=checkbox]:before,
+ .checkbox-group input[type=checkbox]:after {
+ content: "";
+ position: absolute;
+ background: var(--borderColor);
+ width: calc(var(--borderWidth) * 3);
+ height: var(--borderWidth);
+ top: 50%;
+ left: 10%;
+ transform-origin: left center;
+ }
+ .checkbox-group input[type=checkbox]:before {
+ transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(0);
+ transition: transform 200ms ease-in 200ms;
+ }
+ .checkbox-group input[type=checkbox]:after {
+ width: calc(var(--borderWidth) * 5);
+ transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(0);
+ transform-origin: left center;
+ transition: transform 200ms ease-in;
+ }
+ .checkbox-group input[type=checkbox]:checked:before {
+ transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(1);
+ transition: transform 200ms ease-in;
+ }
+ .checkbox-group input[type=checkbox]:checked:after {
+ width: calc(var(--borderWidth) * 5);
+ transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(1);
+ transition: transform 200ms ease-out 200ms;
+ }
+ .checkbox-group input[type=checkbox]:focus {
+ outline: calc(var(--borderWidth) / 2) dotted rgba(0, 0, 0, 0.25);
+ }
+
+.checkbox-container{
+ margin-top: 8px;
display: flex;
- flex-direction: column;
- gap: 10px;
- align-items: flex-start;
+ justify-items: left;
+ align-items: center;
+ gap: 5px;
}
-
+
+
.submit-btn {
+ display: flex;
+ justify-content: center;
padding: 10px 20px;
- margin-top: 10px;
+ margin-left: 80px;
+ margin-top: 30px;
margin-bottom: 10px;
background-color: #007BFF;
color: white;
@@ -126,13 +196,12 @@ gap: 5px;
}
.mitte {
- background-color: #FDFCFA;
+ background-color: #FFF;
display: flex;
width: 700px;
flex-direction: column;
align-items: center;
- padding: 5% 50px;
- margin-top: 20px;
+ padding: 40px;
gap: 10px;
border: 0px;
border-color: black;
@@ -141,14 +210,11 @@ gap: 5px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
}
-h1 {
- align-content: center;
-}
-
.progressbar{
position: relative;
width: 210px;
height: 30px;
+ margin: 50px 20px 5px 20px;
background: rgb(42, 46, 78);
border-radius: 5px;
overflow: hidden;
@@ -173,9 +239,10 @@ h1 {
.dropdownMenus {
display: flex;
- justify-content: flex-end;
+ justify-content: center;
margin-top: 1px;
- gap: 150px;
+ margin-bottom: 30px;
+ gap: 170px;
padding: 2px 10px 2px 10px;
}
@@ -184,5 +251,229 @@ h1 {
}
.labelDiv {
- gap: 200px;
+ gap: 60px;
+ display: flex;
+ justify-content: center;
+ overflow-wrap:inherit;
+ padding-bottom: 20px;
+ margin-top: 40px;
+ margin-bottom: 10px;
+}
+
+/*Step bar*/
+.step-nav {
+ display: flex;
+ gap: 20px;
+ justify-content: center;
+ background: #fff;
+ padding: 10px 30px;
+ border-radius: 6px;
+ box-shadow: 0 4px 10px rgba(0,0,0,0.1);
+}
+
+.step-item {
+ padding: 10px 25px;
+ border-radius: 8px;
+ background: #eee;
+ cursor: pointer;
+ font-weight: bold;
+ transition: 0.2s;
+}
+
+.step-item.active {
+ background: #007BFF;
+ color: white;
+}
+
+.step-item:hover {
+ background: #d9d9d9;
+}
+
+/*panels*/
+.step {
+ margin-top: 70px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 400px;
+}
+
+/*Navigation arrows*/
+.step-nav-arrows {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.middle-container-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 30px;
+ width: max-content;
+ height: auto;
+}
+
+.navBtn {
+ display: flex;
+ justify-content: center;
+ padding: 10px 25px;
+ background-color: #007BFF;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.navBtn:disabled {
+ background-color: #ccc;
+ cursor: not-allowed;
+}
+
+.testy{
+ background-color: #FFF;
+ display: flex;
+ width: auto;
+ flex-direction: row;
+ align-items: center;
+ margin-top: 20px;
+ gap: 10px;
+ border: 0px;
+ border-color: black;
+ border-style: solid;
+ border-radius: 6px;
+ visibility: hidden;
+}
+
+.box2 {
+ background-color: red;
+ width: 40px;
+ height: 40px;
+ padding: 5px;
+ border: 1px solid black;
+ margin: 5px;
+ border-radius: 100px;
+}
+
+li {
+ color: #FFF;
+}
+
+.p-menu1 {
+ margin-left: 20px;
+ z-index: 10;
+}
+
+.hamburger1 {
+ height: 45px;
+ margin: 0;
+ padding-top: 8px;
+ display: grid;
+ grid-template-rows: repeat(3, 1fr);
+ justify-items: center;
+ z-index: 120;
+}
+
+.hamburger1 div {
+ background-color: rgb(61, 61, 61);
+ position: relative;
+ width: 40px;
+ height: 5px;
+ margin-top: 0;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+}
+
+#toggle1 {
+ display: none;
+}
+
+#toggle1:checked + .hamburger1 .top {
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ margin-top: 22.5px;
+}
+
+#toggle1:checked + .hamburger1 .meat {
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+ margin-top: -5px;
+}
+
+#toggle1:checked + .hamburger1 .bottom {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+}
+
+#toggle1:checked ~ .menu1 {
+ height: 150px;
+ width: 300px;
+}
+
+/* Menu */
+.menu1 {
+ position: absolute;
+ top: 55px;
+ left: 20px;
+ width: 240px;
+ border-radius: 5px;
+ background-color: #1C3B69;
+ margin: 0;
+ display: -ms-grid;
+ display: grid;
+ grid-template-rows: 1fr repeat(4, 0.5fr);
+ grid-row-gap: 25px;
+ padding: 0;
+ list-style: none;
+ clear: both;
+ width: auto;
+ text-align: center;
+ height: 0px;
+ overflow: hidden;
+ transition: height 0.3s ease, width 0.3s ease;
+ z-index: 9999;
+ -webkit-transition: all 0.3s ease;
+}
+
+.menu1 li:first-child {
+ margin-top: 30px;
+}
+
+.menu1 li:last-child {
+ margin-bottom: 30px;
+}
+
+.li1 {
+ width: 100%;
+ margin: 0;
+ padding: 10px 0;
+ font: 700 20px 'Oswald', sans-serif;
+}
+
+.li1:hover {
+ background-color: #FFF;
+ color: rgb(61, 61, 61);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ -webkit-transition: all 0.3s ease;
+ transition: all 0.3s ease;
+}
+
+#step2, #step3, #step5 {
+ font-size: larger;
+}
+
+.download-btn {
+ display: flex;
+ justify-content: center;
+ padding: 10px 20px;
+ margin-left: 80px;
+ margin-top: 30px;
+ margin-bottom: 10px;
+ background-color: #007BFF;
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
}
\ No newline at end of file
diff --git a/main.js b/main.js
index 7a8e730..6be3875 100644
--- a/main.js
+++ b/main.js
@@ -61,7 +61,7 @@ let mainWindow;
function createWindow() {
mainWindow = new electron.BrowserWindow({
- width: 800,
+ width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
@@ -124,10 +124,12 @@ electron.ipcMain.handle('get-module-names', async () => {
// mainWindow.webContents.send("modules", module_array)
// })
-
+var globalArgs = {}
+var globalFinalHtmlPath = ""
electron.ipcMain.on("file_submit", async (event, args) => {
try {
+ globalArgs = args
let curstep = 0
let totalsteps = 3 + args.document.styles.length
@@ -185,9 +187,9 @@ electron.ipcMain.on("file_submit", async (event, args) => {
for (let i = 0; i < args.document.styles.length; i++) {
console.log(`\n\n Running the LLM for Document Style ${i+1}`);
- await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/meetingReport.json", language: "en"}).then(resp => {
+ await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/standard_meeting_report.txt", language: "en"}).then(resp => {
console.log(resp);
- transcriptpath = resp
+ globalFinalHtmlPath = resp
curstep++
mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps})
}).catch(err => {
@@ -225,6 +227,9 @@ electron.ipcMain.on("file_submit", async (event, args) => {
}
})
+electron.ipcMain.on("file_download", async() => {
+ await mapFunctions.get("htmlDocumentConverter").convert({inputPath:globalFinalHtmlPath, format: globalArgs.document.outputType, showDialog: true});
+})
let q =
diff --git a/requires.js b/requires.js
index da3b9aa..6a7e20a 100644
--- a/requires.js
+++ b/requires.js
@@ -6,6 +6,9 @@ platform = process.platform
mainDir = __dirname
fs = require("fs")
readline = require("readline")
+
+puppeteer = require("puppeteer")
+htmltodocx = require("html-to-docx")
config = require("./config/config")
ffmpegPath = require('ffmpeg-static');
diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js
index d07a422..4b7c89b 100644
--- a/services/modules/llm-chat_gpt/chatgpt.js
+++ b/services/modules/llm-chat_gpt/chatgpt.js
@@ -1,7 +1,7 @@
const fs = require('fs');
const path = require('path');
-const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
+const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
@@ -9,8 +9,7 @@ if (!fs.existsSync(outputDir)) {
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here"
const SAIA_API_KEY = process.env.SAIA_API_KEY; // Ensure SAIA API key is set in environment variables
-
-const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; //URL for the REST call, used model and action
+const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action
const module_exports = {
name: "llm-saia_openai_gpt",
@@ -19,66 +18,72 @@ const module_exports = {
description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform",
async function(parameter) {
- try {
- console.log("SAIA OpenAI GPT module invoked with parameters:", parameter);
+ return new Promise(async (resolve, reject) => {
+ try {
+ // console.log("SAIA OpenAI GPT module invoked with parameters:", parameter);
- await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
- parameter.inputTranscriptPath, // Path to input transcript file
- parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
- parameter.language // Language for the document which is chosen in the front end by the user
- );
+ resolve(await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
+ parameter.inputTranscriptPath, // Path to input transcript file
+ parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
+ parameter.language // Language for the document which is chosen in the front end by the user
+ ));
+
+ } catch (error) {
+ // console.error("Error in SAIA OpenAI GPT module:", error);
+ reject(error)
+ }
+ })
- } catch (error) {
- console.error("Error in SAIA OpenAI GPT module:", error);
- }
},
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
- try {
- const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
- const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
- const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
+ return new Promise(async(resolve, reject) => {
+ try {
+ const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
+ const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
+ const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
- // --- REST CALL ---
- const response = await fetch(SAIA_URL, {
- method: "POST",
- headers: {
- "Authorization": `Bearer ${SAIA_API_KEY}`,
- "Accept": "application/json",
- "Content-Type": "application/json"
- },
- body: JSON.stringify({
- model: "openai-gpt-oss-120b",
- messages: [
- { role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
- { role: "user", content: promptText }
- ],
- temperature: 0
- })
- });
+ // --- REST CALL ---
+ const response = await fetch(SAIA_URL, { //safe model response in variable
+ method: "POST",
+ headers: {
+ "Authorization": `Bearer ${SAIA_API_KEY}`,
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ model: "openai-gpt-oss-120b",
+ messages: [
+ { role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
+ { role: "user", content: promptText }
+ ],
+ temperature: 0
+ })
+ });
- if (!response.ok) { //ok is true when a response was successful
- const text = await response.text();
- throw new Error(`SAIA API error (${response.status}): ${text}`);
+ if (!response.ok) { //ok is true when a responce was successfull
+ const text = await response.text();
+ throw new Error(`SAIA API error (${response.status}): ${text}`);
+ }
+
+ const data = await response.json();
+
+ // Get generated text from response or default to empty string (if null)
+ // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
+ const output = data.choices?.[0]?.message?.content || "";
+ let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
+ // console.log(inputTranscriptName);
+ const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
+ fs.writeFileSync(outPath, output, "utf8"); // Write output to file
+
+ // console.log("Generated document written to:", outPath);
+ resolve(outPath)
+
+ } catch (error) {
+ // console.error("Error generating SAIA content:", error);
+ reject(error)
}
-
- const data = await response.json();
-
- // Get generated text from response or default to empty string (if null)
- // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
- const output = data.choices?.[0]?.message?.content || "";
-
- let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
- console.log(inputTranscriptName);
-
- const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
- fs.writeFileSync(outPath, output, "utf8"); // Write output to file
-
- console.log("Generated document written to:", outPath);
-
- } catch (error) {
- console.error("Error generating SAIA content:", error);
- }
+ })
}
};
diff --git a/services/modules/llm-gemini/gemini.js b/services/modules/llm-gemini/gemini.js
index cff1026..beff750 100644
--- a/services/modules/llm-gemini/gemini.js
+++ b/services/modules/llm-gemini/gemini.js
@@ -71,7 +71,7 @@ const module_exports = {
const output = data?.candidates?.[0]?.content?.parts?.[0]?.text || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
// console.log(inputTranscriptName);
- const outPath = path.join(outputDir, `${inputTranscriptName}.md`); // Output file path & name to make naming dynamic. Pulled from input transcript name
+ const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
// console.log("Generated document written to:", outPath);
diff --git a/services/modules/quen3/quen3.js b/services/modules/quen3/quen3.js
index fd93737..0bc913a 100644
--- a/services/modules/quen3/quen3.js
+++ b/services/modules/quen3/quen3.js
@@ -18,66 +18,72 @@ const module_exports = {
description: "Generates documents using QWEN 3 235B via SAIA platform",
async function(parameter) {
- try {
- console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter);
+ return new Promise(async (resolve, reject) => {
+ try {
+ // console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter);
- await this.createDocumentFromTranscript( // Call the function to create document with transcript, document type and language
- parameter.inputTranscriptPath, // Path to input transcript file
- parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
- parameter.language // Language for the document which is chosen in the front end by the user
- );
+ resolve(await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
+ parameter.inputTranscriptPath, // Path to input transcript file
+ parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
+ parameter.language // Language for the document which is chosen in the front end by the user
+ ));
+
+ } catch (error) {
+ // console.error("Error in SAIA QWEN 3 235B module:", error);
+ reject(error)
+ }
+ })
- } catch (error) {
- console.error("Error in SAIA QWEN 3 235B module:", error);
- }
},
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
- try {
- const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); // read transcript file from Path
- const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); // read document type from Path
- const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; // combine doc type, language and transcript - Change prompt here if needed
+ return new Promise(async(resolve, reject) => {
+ try {
+ const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
+ const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
+ const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
- // --- REST CALL ---
- const response = await fetch(SAIA_URL, {
- method: "POST",
- headers: {
- "Authorization": `Bearer ${SAIA_API_KEY}`,
- "Accept": "application/json",
- "Content-Type": "application/json"
- },
- body: JSON.stringify({
- model: "qwen3-235b-a22b",
- messages: [
- { role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
- { role: "user", content: promptText }
- ],
- temperature: 0
- })
- });
+ // --- REST CALL ---
+ const response = await fetch(SAIA_URL, { //safe model response in variable
+ method: "POST",
+ headers: {
+ "Authorization": `Bearer ${SAIA_API_KEY}`,
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ model: "qwen3-235b-a22b",
+ messages: [
+ { role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
+ { role: "user", content: promptText }
+ ],
+ temperature: 0
+ })
+ });
- if (!response.ok) { // ok is true when a response was successful
- const text = await response.text();
- throw new Error(`SAIA API error (${response.status}): ${text}`);
+ if (!response.ok) { //ok is true when a responce was successfull
+ const text = await response.text();
+ throw new Error(`SAIA API error (${response.status}): ${text}`);
+ }
+
+ const data = await response.json();
+
+ // Get generated text from response or default to empty string (if null)
+ // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
+ const output = data.choices?.[0]?.message?.content || "";
+ let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
+ // console.log(inputTranscriptName);
+ const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
+ fs.writeFileSync(outPath, output, "utf8"); // Write output to file
+
+ // console.log("Generated document written to:", outPath);
+ resolve(outPath)
+
+ } catch (error) {
+ // console.error("Error generating SAIA content:", error);
+ reject(error)
}
-
- const data = await response.json();
-
- // Get generated text from response or default to empty string (if null)
- // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
- const output = data.choices?.[0]?.message?.content || "";
-
- let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
- console.log(inputTranscriptName);
-
- const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
- fs.writeFileSync(outPath, output, "utf8"); // Write output to file
-
- console.log("Generated document written to:", outPath);
-
- } catch (error) {
- console.error("Error generating SAIA content:", error);
- }
+ })
}
};
diff --git a/storage/.DS_Store b/storage/.DS_Store
index 3c42b1f..7cbbcbf 100644
Binary files a/storage/.DS_Store and b/storage/.DS_Store differ
diff --git a/storage/documentType/agenda.txt b/storage/documentType/agenda.txt
new file mode 100644
index 0000000..7a94443
--- /dev/null
+++ b/storage/documentType/agenda.txt
@@ -0,0 +1,25 @@
+Du bist ein erfahrener Moderator und Projektmanager.
+
+AUFGABE:
+Erstelle eine sinnvolle Meeting-Agenda basierend auf dem folgenden Transkript.
+
+ANFORDERUNGEN:
+- Rekonstruiere die tatsächlichen Themenblöcke
+- Ordne sie logisch und chronologisch
+- Fasse ähnliche Diskussionen zusammen
+- Keine irrelevanten Details aufnehmen
+
+STRUKTUR:
+- Titel der Agenda
+- Ziel des Meetings (1–2 Sätze)
+- Agenda-Punkte (nummeriert)
+ - Thema
+ - Kurzbeschreibung
+ - Ziel des Punktes (Information, Entscheidung, Diskussion)
+
+STIL:
+- Klar, kompakt
+- Business-orientiert
+- Keine Sprecher- oder Zeitangaben
+
+TRANSKRIPT:
diff --git a/storage/documentType/custom_document.txt b/storage/documentType/custom_document.txt
new file mode 100644
index 0000000..674c72c
--- /dev/null
+++ b/storage/documentType/custom_document.txt
@@ -0,0 +1,21 @@
+Du bist ein intelligenter Dokumenten-Generator.
+
+AUFGABE:
+Erstelle ein individuelles Dokument basierend auf:
+1) dem Meeting-Transkript
+2) der zusätzlichen Nutzeranweisung
+
+WICHTIG:
+- Priorisiere die Nutzeranweisung
+- Nutze das Transkript als Wissensquelle
+- Struktur, Tonalität und Detailgrad anpassen
+- Inhalte logisch zusammenführen
+
+FORMAT:
+- Passe Struktur und Stil an den Nutzerwunsch an
+- Klare Überschriften
+- Keine Sprecher- oder Zeitangaben
+
+TRANSKRIPT & NUTZERANWEISUNG:
+
+
diff --git a/storage/documentType/meetingReport.json b/storage/documentType/meetingReport.json
deleted file mode 100644
index 04626df..0000000
--- a/storage/documentType/meetingReport.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
- "FORMAT": "markdown",
- "GOAL":"Generate a structured meeting report (Markdown). **Output ONLY:** final .md. No meta.",
- "STRUCTURE": {
- "titlepage": ["title","date","start","end","duration","location","host","participants"],
- "toc": "[section](#anchor) — HH:MM:SS",
- "section": {
- "h2": "
— HH:MM:SS",
- "summary": "1 sentence",
- "key_points": "<=5 bullets, quotes optional",
- "decisions": "list: text | owner | due",
- "actions": "table: id | task | owner | due | status"
- },
- "exec_summary": "3 short sentences",
- "consolidated": ["decisions", "actions"],
- "appendix": "optional"
- },
- "STYLE": {
- "tone": "neutral, concise",
- "ts_format": "HH:MM:SS",
- "no_meta": true
- },
- "PROCESS": {
- "timestamps": "use if present; else estimate minimal",
- "speakers": "use labels; else Speaker X",
- "long_transcripts": "chunk → summarize → merge",
- "unclear": "UNKLAR:"
- },
-
- "JSON_OUTPUT_OPTIONAL": true,
-
- "PROMPT_SNIPPET": "Generate meeting report in markdown using STRUCTURE and STYLE. Output only the report."
-}
\ No newline at end of file
diff --git a/storage/documentType/result_protocol.txt b/storage/documentType/result_protocol.txt
new file mode 100644
index 0000000..caf9570
--- /dev/null
+++ b/storage/documentType/result_protocol.txt
@@ -0,0 +1,25 @@
+Du bist ein professioneller Protokollführer.
+
+AUFGABE:
+Erstelle ein Ergebnisprotokoll basierend auf dem Meeting-Transkript.
+
+FOKUS:
+- Ergebnisse statt Diskussionen
+- Entscheidungen, Beschlüsse, Vereinbarungen
+- Klare, überprüfbare Aussagen
+
+STRUKTUR:
+1. Meeting-Informationen
+2. Ergebnisse je Thema
+ - Thema
+ - Ergebnis / Beschluss
+3. Entscheidungen
+4. Aufgaben & Verantwortlichkeiten
+5. Offene Punkte
+
+REGELN:
+- Keine Meinungen oder Spekulationen
+- Keine Zeit- oder Sprecherangaben
+- Sachlich, formal
+
+TRANSKRIPT:
diff --git a/storage/documentType/sprint_planning_note.txt b/storage/documentType/sprint_planning_note.txt
new file mode 100644
index 0000000..df210e8
--- /dev/null
+++ b/storage/documentType/sprint_planning_note.txt
@@ -0,0 +1,34 @@
+Du bist ein erfahrener Scrum Master.
+
+AUFGABE:
+Erstelle Sprint Planning Notes aus dem folgenden Meeting-Transkript.
+
+FOKUS:
+- Sprint-Ziele
+- User Stories / Tasks
+- Abhängigkeiten
+- Risiken
+- Commitments
+
+STRUKTUR:
+1. Sprint Overview
+ - Sprint-Ziel
+ - Zeitraum (falls erwähnt)
+
+2. Geplante Arbeit
+ - User Story / Task
+ - Beschreibung
+ - Akzeptanzkriterien (falls ableitbar)
+
+3. Abhängigkeiten & Blocker
+
+4. Risiken & Annahmen
+
+5. Vereinbarungen / Team-Commitments
+
+STIL:
+- Agile-konform
+- Klar & umsetzungsorientiert
+- Bullet Points bevorzugen
+
+TRANSKRIPT:
diff --git a/storage/documentType/standard_meeting_report.txt b/storage/documentType/standard_meeting_report.txt
index 7a202f1..c6d9d18 100644
--- a/storage/documentType/standard_meeting_report.txt
+++ b/storage/documentType/standard_meeting_report.txt
@@ -1,217 +1,44 @@
-Generate a structured meeting report in HTML using STRUCTURE and STYLE.
-Output ONLY the final .md document — no meta comments, no explanations.
+Du bist ein professioneller Meeting-Analyst und Business Writer.
-Follow exactly the STRUCTURE defined below.
-Follow exactly the STYLE rules.
-Use timestamps in HH:MM:SS format.
-If information is missing, use: Unclear:.
+AUFGABE:
+Erstelle einen strukturierten Follow-up Report basierend auf dem folgenden Meeting-Transkript.
-==================== STRUCTURE & RULES ====================
+ANFORDERUNGEN:
+- Fasse Inhalte sinngemäß zusammen
+- Entferne Redundanzen und Smalltalk
+- Formuliere klar, präzise und professionell
+- Verwende neutrale Business-Sprache
+- Keine Zeitstempel oder Sprecher-Namen zitieren
+- Leite Entscheidungen und Aufgaben logisch ab, wenn sie implizit sind
+- Markiere offene Punkte klar
-{
- "FORMAT": "HTML",
+STRUKTUR DES DOKUMENTS:
+1. Titel & Metadaten
+ - Meetingtitel (ableiten)
+ - Datum (falls im Transkript erwähnt, sonst „nicht angegeben“)
+ - Teilnehmer (zusammengefasst)
- "STRUCTURE": {
- "titlepage": [
- "title",
- "date",
- "start",
- "end",
- "duration",
- "location",
- "host",
- "participants"
- ],
+2. Executive Summary (max. 5 Bullet Points)
- "toc": "[section](#anchor) — HH:MM:SS",
+3. Besprochene Themen
+ - Thema
+ - Kernaussagen
+ - Relevante Erkenntnisse
- "section": {
- "h2": " — HH:MM:SS",
- "summary": "exactly 1 concise sentence",
- "key_points": "maximum 5 bullet points; quotes optional",
- "decisions": "list items formatted as: decision text | owner | due date",
- "actions": "HTML table: id | task | owner | due | status"
- },
+4. Entscheidungen
+ - Entscheidung
+ - Kontext / Begründung
- "exec_summary": "exactly 3 short sentences",
+5. Action Items
+ - Aufgabe
+ - Verantwortlich (falls ableitbar)
+ - Ziel / Zweck
- "consolidated": [
- "decisions",
- "actions"
- ],
+6. Offene Fragen & Risiken
- "appendix": "optional"
- },
+STIL:
+- Überschriften klar strukturiert
+- Bullet Points bevorzugen
+- Präzise, keine Umgangssprache
- "STYLE": {
- "tone": "neutral, concise, professional",
- "ts_format": "HH:MM:SS",
- "no_meta": true
- },
-
- "PROCESS": {
- "timestamps": "use transcript timestamps if present; otherwise estimate minimal",
- "speakers": "use names if available; else Speaker X",
- "long_transcripts": "split → summarize → merge",
- "unclear": "Unclear:"
- },
-
- "PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
-}
-
-============================================================
-
-Insert all generated content into the following HTML TEMPLATE:
-
-# {{title}}
-
-**Date:** {{date}}
-**Start:** {{start}}
-**End:** {{end}}
-**Duration:** {{duration}}
-**Location:** {{location}}
-**Host:** {{host}}
-**Participants:** {{participants}}
-
----
-
-## Table of Contents
-{{toc}}
-
----
-Generate a structured meeting report in HTML using STRUCTURE and STYLE.
-Output ONLY the final .md document — no meta comments, no explanations.
-
-Follow exactly the STRUCTURE defined below.
-Follow exactly the STYLE rules.
-Use timestamps in HH:MM:SS format.
-If information is missing, use: UNKLAR:.
-
-==================== STRUCTURE & RULES ====================
-
-{
- "FORMAT": "HTML",
-
- "STRUCTURE": {
- "titlepage": [
- "title",
- "date",
- "start",
- "end",
- "duration",
- "location",
- "host",
- "participants"
- ],
-
- "toc": "[section](#anchor) — HH:MM:SS",
-
- "section": {
- "h2": " — HH:MM:SS",
- "summary": "exactly 1 concise sentence",
- "key_points": "maximum 5 bullet points; quotes optional",
- "decisions": "list items formatted as: decision text | owner | due date",
- "actions": "HTML table: id | task | owner | due | status"
- },
-
- "exec_summary": "exactly 3 short sentences",
-
- "consolidated": [
- "decisions",
- "actions"
- ],
-
- "appendix": "optional"
- },
-
- "STYLE": {
- "tone": "neutral, concise, professional",
- "ts_format": "HH:MM:SS",
- "no_meta": true
- },
-
- "PROCESS": {
- "timestamps": "use transcript timestamps if present; otherwise estimate minimal",
- "speakers": "use names if available; else Speaker X",
- "long_transcripts": "split → summarize → merge",
- "unclear": "UNKLAR:"
- },
-
- "PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
-}
-
-============================================================
-
-Insert all generated content into the following HTML TEMPLATE:
-
-# {{title}}
-
-**Date:** {{date}}
-**Start:** {{start}}
-**End:** {{end}}
-**Duration:** {{duration}}
-**Location:** {{location}}
-**Host:** {{host}}
-**Participants:** {{participants}}
-
----
-
-## Table of Contents
-{{toc}}
-
----
-
-## Executive Summary
-{{exec_summary}}
-
----
-
-## Sections
-{{sections}}
-
----
-
-## Consolidated Decisions
-{{consolidated_decisions}}
-
----
-
-## Consolidated Actions
-{{consolidated_actions}}
-
----
-
-## Appendix
-{{appendix}}
-
-============================================================
-
-Final Requirement:
-Output ONLY the completed HTML meeting report.
-## Executive Summary
-{{exec_summary}}
-
----
-
-## Sections
-{{sections}}
-
----
-
-## Consolidated Decisions
-{{consolidated_decisions}}
-
----
-
-## Consolidated Actions
-{{consolidated_actions}}
-
----
-
-## Appendix
-{{appendix}}
-
-============================================================
-
-Final Requirement:
-Output ONLY the completed HTML meeting report.
\ No newline at end of file
+TRANSKRIPT: