Merge branch 'develop' into 'feature/38-sprecher-audio-snippets-s4-11'

# Conflicts:
#   main.js
This commit is contained in:
Spanier, Pit
2025-12-16 16:18:17 +01:00
17 changed files with 1039 additions and 452 deletions
+119 -28
View File
@@ -8,29 +8,41 @@
</head> </head>
<body> <body>
<div class="mitte" id="mitte"> <div id="h1-wrapper">
<div class="labelDiv" id="labelDiv"> <section class="p-menu1">
<label id="labelKI">Select ki:</label> <nav id="navbar" class="navigation" role="navigation">
<label id="labelTranscription">Select transcription:</label> <input id="toggle1" type="checkbox" />
<label id="labelLanguage">Select language:</label> <label class="hamburger1" for="toggle1">
<img id="labelLanguageFlag" src="flags/united-kingdom-flag-png-large.jpg" width="20" height="10" > <div class="top"></div>
</div> <div class="meat"></div>
<div class="dropdownMenus" id="dropdownMenus"> <div class="bottom"></div>
<select name="ai_type" id="ai_type"> </label>
</select>
<select name="transkript_type" id="transkript_type"> <nav class="menu1">
</select> <li class="li1">Help</li>
<select name="output_type" id="output_type"> <li class="li1">Language</li>
<option value="flac">flac</option> </nav>
<option value="mp3">mp3</option> </nav>
<option value="wav">wav</option> </section>
</select>
<select name="language_option" id="language_option">
</select>
</div>
<h1 id="h1">Video to document</h1> <h1 id="h1">Video to document</h1>
</div>
<div class="step-nav">
<div class="step-item active" data-step="1">1. Step</div>
<div class="step-item" data-step="2">2. Step</div>
<div class="step-item" data-step="3">3. Step</div>
<div class="step-item" data-step="4">4. Step</div>
<div class="step-item" data-step="5">5. Step</div>
<div class="step-item" data-step="6">6. Step</div>
</div>
<div id="middleContainerWrapper" class="middle-container-wrapper">
<button id="prevBtn" class="navBtn" disabled>&larr;</button>
<div class="mitte" id="mitte">
<div class="step" id="step1">
<div class="upload-container" id="uploadContainer"> <div class="upload-container" id="uploadContainer">
<p id="p1">Drag and drop video file</p> <p id="p1">Drag and drop video file</p>
<video id="previewThumbnail" autoplay="false"> <video id="previewThumbnail" autoplay="false">
@@ -42,26 +54,105 @@
<button class="custom-btn" id="manualUploadBtn">Search video</button> <button class="custom-btn" id="manualUploadBtn">Search video</button>
<input type="file" id="videoUpload" accept="video/*"> <input type="file" id="videoUpload" accept="video/*">
</div> </div>
</div>
<div class="step" id="step2" style="display:none;">
<div class="labelDiv" id="labelDiv">
<label id="labelKI">Select ki:</label>
<label id="labelTranscription">Select transcription:</label>
<label id="labelType">Select type:</label>
<label id="labelLanguage">Select language:</label>
<img id="labelLanguageFlag" src="flags/united-kingdom-flag-png-large.jpg" width="20" height="10" >
</div>
<div class="dropdownMenus" id="dropdownMenus">
<select name="ai_type" id="ai_type">
</select>
<select name="transkript_type" id="transkript_type">
</select>
<select name="output_type" id="output_type">
<option value="pdf">.pdf</option>
<option value="word">.word</option>
<option value="txt">.txt</option>
</select>
<select name="language_option" id="language_option">
</select>
</div>
</div>
<div class="step" id="step3" style="display:none;">
<div class="checkbox-group"> <div class="checkbox-group">
<label id="checkbox_group" for="checkbox-group">Choose prefered document style:</label> <label id="checkbox-label" for="checkbox-group">Choose prefered document style:</label>
<div class="checkbox-container"> <div class="checkbox-container">
<input type="checkbox" name ="docFormat" id="docFormat" value="Meeting report"> <input type="checkbox" name ="docFormat" id="docFormat" value="Meeting report">
<label id="label_format" for="docFormat">Meeting report</label> <label id="label_format" for="docFormat">Follow-up Report</label>
</div> </div>
<div class="checkbox-container"> <div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary" value="Summary with timestamps"> <input type="checkbox" name="docFormat" id="docFormatSummary1" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Summary with timestamps</label> <label id="label_summary" for="docFormatSummary">Agenda</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary2" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Resultprotocol</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary3" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Sprint Planning Note</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatCustom" value="Summary with timestamps">
<select name="ai_type" id="ai_type">
<option>nichts</option>
</select>
</div>
</div> </div>
</div> </div>
<div class="step" id="step4" style="display:none;">
<button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button> <button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button>
<div class="progressbar" id="progressbar"> <div class="testy" id="testy">
<div class="progress_fill"></div> <div class="box2" id="box1">
<span class="progress_text">0%</span>
</div> </div>
<p>---Starting---</p>
<div class="box2" id="box2">
</div>
<p>---Transkribing---</p>
<div class="box2" id="box3">
</div>
<p>---Document creation---</p>
<div class="box2" id="box4">
</div>
</div>
</div>
<div class="step" id="step5" style="display:none;">
<div class="speakerView" id="speakerView">
<label id="labelSpeaker">Select Speaker:</label>
<select name="cur_speaker" id="cur_speaker">
<options>Stefan</options>
</select>
</div>
<div class="speakerAudio" id="speakerAutio">
<label id="labelSpeakerAudio">Selected Speaker</label>
<audio controls id="speakerAudioViewer">
Currently there is no audio file here.
</audio>
</div>
<div class="speakerWrite" id="speakerWrite">
<label id="labelSpeakerWriter">Write name:</label>
<input type="text" id="newSpeaker">
</div>
<button id="speakerLocker" onclick="rewriteSpeakerName()">Rename Speaker</button>
<button id="speakerResender" onclick="sendSpeakerPackages()">Rewrite document</button>
</div>
<div class="step" id="step6" style="display:none;">
<button class="download-btn" id="downloadButton" onclick="fileDownload()">Download</button>
</div>
</div>
<button id ="nextBtn" class="navBtn">&rarr;</button>
</div> </div>
<script src="languages.js"></script> <script src="languages.js"></script>
+14 -4
View File
@@ -15,12 +15,22 @@ try {
getModuleNames: () => ipcRenderer.invoke('get-module-names') getModuleNames: () => ipcRenderer.invoke('get-module-names')
}) })
contextBridge.exposeInMainWorld('electron', {
progress: (callback) => ipcRenderer.on('progress', callback)
ipcRenderer.on("progress", (event, resp) => {
alert(`Finished step ${resp.curstep} of ${resp.totalsteps}`)
}) })
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)}) ipcRenderer.on("error", (event, err) => {alert(err)})
} catch (error) { } catch (error) {
console.log("Error in preload.js"); console.log("Error in preload.js");
+170 -14
View File
@@ -33,7 +33,6 @@ uploadContainer.addEventListener("drop", (e) => {
window.addEventListener('load', async (e) => { window.addEventListener('load', async (e) => {
try { try {
console.log("test");
loadLanguageOptions(); loadLanguageOptions();
const value = await window.onStartup.getModuleNames(); const value = await window.onStartup.getModuleNames();
loadAiOptions(value.ai_modules); 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", () => { videoUpload.addEventListener("change", () => {
try { try {
activateSubmitBtn(videoUpload.files.length > 0); if (videoUpload.files.length > 0) {
const file = videoUpload.files;
handleFiles(file);
}
} catch (error) { } catch (error) {
console.log(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 //listener for the file explorer search
@@ -85,3 +76,168 @@ manualUploadBtn.addEventListener('click', () => {
} }
}); });
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) {
}
};
+128 -7
View File
@@ -1,4 +1,4 @@
let currentVideoPath = null;
//function to check if one checkbox is at least klicked //function to check if one checkbox is at least klicked
function checkBoxes() { function checkBoxes() {
try { try {
@@ -25,7 +25,6 @@ function checkBoxes() {
var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]); var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]);
var pathToLower = pathTest.toLowerCase(); var pathToLower = pathTest.toLowerCase();
if(testEndings.some(e => pathToLower.endsWith(e))){ if(testEndings.some(e => pathToLower.endsWith(e))){
document.getElementById("progressbar").style.visibility = "visible";
//assembly of the json for the main //assembly of the json for the main
const selectedStyles = [checkedCounter]; const selectedStyles = [checkedCounter];
@@ -37,6 +36,11 @@ function checkBoxes() {
iter++; 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); console.log(selectedCheckboxes);
const outputType = document.getElementById("output_type"); const outputType = document.getElementById("output_type");
const transcriptionType = document.getElementById("transkript_type"); const transcriptionType = document.getElementById("transkript_type");
@@ -44,15 +48,15 @@ function checkBoxes() {
const sendingPackage = { const sendingPackage = {
"video": { "video": {
"module":"extraction-video-to-audio", "module":"extraction-video-to-audio",
"inputVideoPath": pathTest, "inputVideoPath": pathTest
"outputType": outputType.value
}, },
"transcription": { "transcription": {
"module": transcriptionType.value "module": transcriptionType.value
}, },
"document": { "document": {
"module":aiType.value, "module":aiType.value,
"styles": selectedStyles "styles": selectedStyles,
"outputType": outputType.value
} }
}; };
window.submit.submit(sendingPackage) window.submit.submit(sendingPackage)
@@ -105,6 +109,7 @@ function handleFiles(files) {
const filePath = window.explorer.onFileDrop(files[0]) const filePath = window.explorer.onFileDrop(files[0])
videoUpload.files = files; videoUpload.files = files;
fileName.textContent = `Chosen video: ${file.name}`; fileName.textContent = `Chosen video: ${file.name}`;
currentVideoPath = filePath;
generateThumbnail(filePath); generateThumbnail(filePath);
activateSubmitBtn(true); 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 to load language options to the drop down list
function loadLanguageOptions(){ function loadLanguageOptions(){
try { try {
@@ -180,16 +204,48 @@ function loadLanguageOptions(){
choice.value = object_holdy[i]; choice.value = object_holdy[i];
menu.appendChild(choice); menu.appendChild(choice);
} }
} catch (error) { } catch (error) {
console.log("Error in script.js loadLanguageOptions function"); console.log("Error in script.js loadLanguageOptions function");
console.log(error); 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){ function activateSubmitBtn(hasFile){
try { try {
submitButton.disabled = !hasFile; document.getElementById("submitButton").disabled = !hasFile;
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
@@ -214,3 +270,68 @@ function generateThumbnail(path){
videoElement.style.maxHeight = 40; videoElement.style.maxHeight = 40;
videoElement.autoplay = false; 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);
}
}
+311 -20
View File
@@ -6,10 +6,32 @@ body {
align-items: center; align-items: center;
height: 100vh; height: 100vh;
background-color: #f2f3f4; background-color: #f2f3f4;
gap: 15px; gap: 10px;
margin: 0; 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 { .upload-container {
background: white; background: white;
@@ -91,25 +113,73 @@ input[type="file"] {
display: none; display: none;
} }
.checkbox-group {
--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{ .checkbox-container{
margin-top: 8px; margin-top: 8px;
display: flex; display: flex;
justify-items: left;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
} }
.checkbox-group {
margin-top: 15px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
align-items: flex-start;
}
.submit-btn { .submit-btn {
display: flex;
justify-content: center;
padding: 10px 20px; padding: 10px 20px;
margin-top: 10px; margin-left: 80px;
margin-top: 30px;
margin-bottom: 10px; margin-bottom: 10px;
background-color: #007BFF; background-color: #007BFF;
color: white; color: white;
@@ -126,13 +196,12 @@ gap: 5px;
} }
.mitte { .mitte {
background-color: #FDFCFA; background-color: #FFF;
display: flex; display: flex;
width: 700px; width: 700px;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 5% 50px; padding: 40px;
margin-top: 20px;
gap: 10px; gap: 10px;
border: 0px; border: 0px;
border-color: black; border-color: black;
@@ -141,14 +210,11 @@ gap: 5px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1); box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
} }
h1 {
align-content: center;
}
.progressbar{ .progressbar{
position: relative; position: relative;
width: 210px; width: 210px;
height: 30px; height: 30px;
margin: 50px 20px 5px 20px;
background: rgb(42, 46, 78); background: rgb(42, 46, 78);
border-radius: 5px; border-radius: 5px;
overflow: hidden; overflow: hidden;
@@ -173,9 +239,10 @@ h1 {
.dropdownMenus { .dropdownMenus {
display: flex; display: flex;
justify-content: flex-end; justify-content: center;
margin-top: 1px; margin-top: 1px;
gap: 150px; margin-bottom: 30px;
gap: 170px;
padding: 2px 10px 2px 10px; padding: 2px 10px 2px 10px;
} }
@@ -184,5 +251,229 @@ h1 {
} }
.labelDiv { .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;
} }
+9 -4
View File
@@ -61,7 +61,7 @@ let mainWindow;
function createWindow() { function createWindow() {
mainWindow = new electron.BrowserWindow({ mainWindow = new electron.BrowserWindow({
width: 800, width: 1200,
height: 800, height: 800,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
@@ -124,10 +124,12 @@ electron.ipcMain.handle('get-module-names', async () => {
// mainWindow.webContents.send("modules", module_array) // mainWindow.webContents.send("modules", module_array)
// }) // })
var globalArgs = {}
var globalFinalHtmlPath = ""
electron.ipcMain.on("file_submit", async (event, args) => { electron.ipcMain.on("file_submit", async (event, args) => {
try { try {
globalArgs = args
let curstep = 0 let curstep = 0
let totalsteps = 3 + args.document.styles.length 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++) { for (let i = 0; i < args.document.styles.length; i++) {
console.log(`\n\n Running the LLM for Document Style ${i+1}`); 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); console.log(resp);
transcriptpath = resp globalFinalHtmlPath = resp
curstep++ curstep++
mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps}) mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps})
}).catch(err => { }).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 = let q =
+3
View File
@@ -6,6 +6,9 @@ platform = process.platform
mainDir = __dirname mainDir = __dirname
fs = require("fs") fs = require("fs")
readline = require("readline") readline = require("readline")
puppeteer = require("puppeteer")
htmltodocx = require("html-to-docx")
config = require("./config/config") config = require("./config/config")
ffmpegPath = require('ffmpeg-static'); ffmpegPath = require('ffmpeg-static');
+17 -12
View File
@@ -9,7 +9,6 @@ if (!fs.existsSync(outputDir)) {
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here" // 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_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 = { const module_exports = {
@@ -19,28 +18,33 @@ const module_exports = {
description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform", description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform",
async function(parameter) { async function(parameter) {
return new Promise(async (resolve, reject) => {
try { try {
console.log("SAIA OpenAI GPT module invoked with parameters:", parameter); // 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 resolve(await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
parameter.inputTranscriptPath, // Path to input transcript file 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.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 parameter.language // Language for the document which is chosen in the front end by the user
); ));
} catch (error) { } catch (error) {
console.error("Error in SAIA OpenAI GPT module:", error); // console.error("Error in SAIA OpenAI GPT module:", error);
reject(error)
} }
})
}, },
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => {
try { try {
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path 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 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 const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
// --- REST CALL --- // --- REST CALL ---
const response = await fetch(SAIA_URL, { const response = await fetch(SAIA_URL, { //safe model response in variable
method: "POST", method: "POST",
headers: { headers: {
"Authorization": `Bearer ${SAIA_API_KEY}`, "Authorization": `Bearer ${SAIA_API_KEY}`,
@@ -57,7 +61,7 @@ const module_exports = {
}) })
}); });
if (!response.ok) { //ok is true when a response was successful if (!response.ok) { //ok is true when a responce was successfull
const text = await response.text(); const text = await response.text();
throw new Error(`SAIA API error (${response.status}): ${text}`); throw new Error(`SAIA API error (${response.status}): ${text}`);
} }
@@ -67,18 +71,19 @@ const module_exports = {
// Get generated text from response or default to empty string (if null) // Get generated text from response or default to empty string (if null)
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
const output = data.choices?.[0]?.message?.content || ""; const output = data.choices?.[0]?.message?.content || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
console.log(inputTranscriptName); // console.log(inputTranscriptName);
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // 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 fs.writeFileSync(outPath, output, "utf8"); // Write output to file
console.log("Generated document written to:", outPath); // console.log("Generated document written to:", outPath);
resolve(outPath)
} catch (error) { } catch (error) {
console.error("Error generating SAIA content:", error); // console.error("Error generating SAIA content:", error);
reject(error)
} }
})
} }
}; };
+1 -1
View File
@@ -71,7 +71,7 @@ const module_exports = {
const output = data?.candidates?.[0]?.content?.parts?.[0]?.text || ""; const output = data?.candidates?.[0]?.content?.parts?.[0]?.text || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
// console.log(inputTranscriptName); // 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 fs.writeFileSync(outPath, output, "utf8"); // Write output to file
// console.log("Generated document written to:", outPath); // console.log("Generated document written to:", outPath);
+17 -11
View File
@@ -18,28 +18,33 @@ const module_exports = {
description: "Generates documents using QWEN 3 235B via SAIA platform", description: "Generates documents using QWEN 3 235B via SAIA platform",
async function(parameter) { async function(parameter) {
return new Promise(async (resolve, reject) => {
try { try {
console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter); // 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 resolve(await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
parameter.inputTranscriptPath, // Path to input transcript file 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.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 parameter.language // Language for the document which is chosen in the front end by the user
); ));
} catch (error) { } catch (error) {
console.error("Error in SAIA QWEN 3 235B module:", error); // console.error("Error in SAIA QWEN 3 235B module:", error);
reject(error)
} }
})
}, },
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => {
try { try {
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path 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 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 const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
// --- REST CALL --- // --- REST CALL ---
const response = await fetch(SAIA_URL, { const response = await fetch(SAIA_URL, { //safe model response in variable
method: "POST", method: "POST",
headers: { headers: {
"Authorization": `Bearer ${SAIA_API_KEY}`, "Authorization": `Bearer ${SAIA_API_KEY}`,
@@ -56,7 +61,7 @@ const module_exports = {
}) })
}); });
if (!response.ok) { // ok is true when a response was successful if (!response.ok) { //ok is true when a responce was successfull
const text = await response.text(); const text = await response.text();
throw new Error(`SAIA API error (${response.status}): ${text}`); throw new Error(`SAIA API error (${response.status}): ${text}`);
} }
@@ -66,18 +71,19 @@ const module_exports = {
// Get generated text from response or default to empty string (if null) // Get generated text from response or default to empty string (if null)
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content // SAIA uses OpenAI-compatible structure: data.choices[x].message.content
const output = data.choices?.[0]?.message?.content || ""; const output = data.choices?.[0]?.message?.content || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
console.log(inputTranscriptName); // console.log(inputTranscriptName);
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // 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 fs.writeFileSync(outPath, output, "utf8"); // Write output to file
console.log("Generated document written to:", outPath); // console.log("Generated document written to:", outPath);
resolve(outPath)
} catch (error) { } catch (error) {
console.error("Error generating SAIA content:", error); // console.error("Error generating SAIA content:", error);
reject(error)
} }
})
} }
}; };
BIN
View File
Binary file not shown.
+25
View File
@@ -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 (12 Sätze)
- Agenda-Punkte (nummeriert)
- Thema
- Kurzbeschreibung
- Ziel des Punktes (Information, Entscheidung, Diskussion)
STIL:
- Klar, kompakt
- Business-orientiert
- Keine Sprecher- oder Zeitangaben
TRANSKRIPT:
+21
View File
@@ -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:
-33
View File
@@ -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": "<topic> — 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:<reason>"
},
"JSON_OUTPUT_OPTIONAL": true,
"PROMPT_SNIPPET": "Generate meeting report in markdown using STRUCTURE and STYLE. Output only the report."
}
+25
View File
@@ -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:
@@ -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:
+34 -207
View File
@@ -1,217 +1,44 @@
Generate a structured meeting report in HTML using STRUCTURE and STYLE. Du bist ein professioneller Meeting-Analyst und Business Writer.
Output ONLY the final .md document — no meta comments, no explanations.
Follow exactly the STRUCTURE defined below. AUFGABE:
Follow exactly the STYLE rules. Erstelle einen strukturierten Follow-up Report basierend auf dem folgenden Meeting-Transkript.
Use timestamps in HH:MM:SS format.
If information is missing, use: Unclear:<reason>.
==================== 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
{ STRUKTUR DES DOKUMENTS:
"FORMAT": "HTML", 1. Titel & Metadaten
- Meetingtitel (ableiten)
- Datum (falls im Transkript erwähnt, sonst „nicht angegeben“)
- Teilnehmer (zusammengefasst)
"STRUCTURE": { 2. Executive Summary (max. 5 Bullet Points)
"titlepage": [
"title",
"date",
"start",
"end",
"duration",
"location",
"host",
"participants"
],
"toc": "[section](#anchor) — HH:MM:SS", 3. Besprochene Themen
- Thema
- Kernaussagen
- Relevante Erkenntnisse
"section": { 4. Entscheidungen
"h2": "<topic> — HH:MM:SS", - Entscheidung
"summary": "exactly 1 concise sentence", - Kontext / Begründung
"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", 5. Action Items
- Aufgabe
- Verantwortlich (falls ableitbar)
- Ziel / Zweck
"consolidated": [ 6. Offene Fragen & Risiken
"decisions",
"actions"
],
"appendix": "optional" STIL:
}, - Überschriften klar strukturiert
- Bullet Points bevorzugen
- Präzise, keine Umgangssprache
"STYLE": { TRANSKRIPT:
"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:<reason>"
},
"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:<reason>.
==================== STRUCTURE & RULES ====================
{
"FORMAT": "HTML",
"STRUCTURE": {
"titlepage": [
"title",
"date",
"start",
"end",
"duration",
"location",
"host",
"participants"
],
"toc": "[section](#anchor) — HH:MM:SS",
"section": {
"h2": "<topic> — 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:<reason>"
},
"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.