Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion td.vue/src/components/Navbar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<b-navbar toggleable="lg" fixed="top" id="navbar">
<b-navbar-brand :to="username ? '/dashboard' : '/'" class="td-brand">
<b-navbar-brand :to="username ? '/dashboard' : '/'" class="td-brand" @click="onLogoClick">
<b-img src="@/assets/threatdragon_logo_image.svg" class="td-brand-img" alt="Threat Dragon Logo" />
Threat Dragon v{{this.$store.state.packageBuildVersion}}{{this.$store.state.packageBuildState}}
</b-navbar-brand>
Expand Down Expand Up @@ -136,7 +136,17 @@ export default {
throw error;
}
});
},
// To reset the title when clicked on the logo.
onLogoClick() {
try {
if (window.electronAPI) {
window.electronAPI.modelClosed('');
}
} catch (err) {
console.error('Failed to reset title on logo click:', err);
}
}
}
};
</script>
67 changes: 57 additions & 10 deletions td.vue/src/desktop/desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ protocol.registerSchemesAsPrivileged([
]);

let runApp = true;
async function createWindow () {
let mainWindow = null;
async function createWindow() {

// Create the browser window
const mainWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
width: 1400,
height: 1000,
show: false,
Expand Down Expand Up @@ -109,6 +110,7 @@ app.on('ready', async () => {
ipcMain.on('model-print', handleModelPrint);
ipcMain.on('model-save', handleModelSave);
ipcMain.on('update-menu', handleUpdateMenu);
ipcMain.on('update-title', handleModelRenameTitle);

createWindow();

Expand All @@ -120,7 +122,7 @@ app.on('ready', async () => {
});

// this is emitted when a 'recent document' is opened
app.on('open-file', function(event, path) {
app.on('open-file', function (event, path) {
// apply custom handler to this event
event.preventDefault();
logger.log.debug('Request to open file from recent documents: ' + path);
Expand All @@ -133,32 +135,77 @@ function handleCloseApp() {
app.quit();
}

function handleModelClosed (_event, fileName) {
function handleModelClosed(_event, fileName) {
logger.log.debug('Close model notification from renderer for file name: ' + fileName);
menu.modelClosed();
try {
if (isMacOS) {
app.setName('OWASP Threat Dragon');
if (mainWindow) {
mainWindow.setRepresentedFilename('');
}
} else {
if (mainWindow) {
mainWindow.setTitle('OWASP Threat Dragon');
}
}
} catch (err) {
console.error('Failed to reset title on model close:', err);
}
}

function handleModelOpenConfirmed (_event, fileName) {
// Makes the title change after the rename.

function handleModelRenameTitle(_event, newTitle) {
try {
if(isMacOS) {
app.setName(newTitle || 'OWASP Threat Dragon');
mainWindow.setRepresentedFilename(newTitle || '');
} else {
mainWindow.setTitle(newTitle || 'OWASP Threat Dragon');
}
} catch (err) {
console.error('Failed to update title : ', err);
}
}

function handleModelOpenConfirmed(_event, fileName) {
logger.log.debug('Open model confirmation from renderer for file name: ' + fileName);
menu.openModel(fileName);
}

function handleModelOpened (_event, fileName) {
logger.log.debug('Open model notification from renderer for file name: ' + fileName);
function handleModelOpened(_event, fileName) {
logger.log.debug('Open model notification from renderer with title: ' + fileName);
menu.modelOpened();

try {
if (isMacOS) {
app.setName(fileName || 'OWASP Threat Dragon');
if (mainWindow) {
mainWindow.setRepresentedFilename(fileName || '');
}
} else {
if (mainWindow) {
mainWindow.setTitle(fileName || 'OWASP Threat Dragon');
}
}
} catch (err) {
console.error('Failed to update title : ', err);
}
}

function handleModelPrint (_event, format) {

function handleModelPrint(_event, format) {
logger.log.debug('Model print request from renderer with printer : ' + format);
menu.modelPrint(format);
}

function handleModelSave (_event, modelData, fileName) {
function handleModelSave(_event, modelData, fileName) {
logger.log.debug('Model save request from renderer with file name : ' + fileName);
menu.modelSave(modelData, fileName);
}

function handleUpdateMenu (_event, locale) {
function handleUpdateMenu(_event, locale) {
logger.log.debug('Re-labeling the menu system for: ' + locale);
menu.setLocale(locale);
let template = menu.getMenuTemplate();
Expand Down
2 changes: 2 additions & 0 deletions td.vue/src/main.desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ const openTmBom = (jsonModel) => {
return tmBom.read(jsonModel);
};



const app = new Vue({
router: router.get(),
store: storeFactory.get(),
Expand Down
24 changes: 24 additions & 0 deletions td.vue/src/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'mutationobserver-shim';
import Vue from 'vue';
import { setPageTitle } from './utils/title.js';

import App from './App.vue';
import i18nFactory from './i18n/index.js';
Expand All @@ -12,6 +13,29 @@ import './plugins/toastification.js';

Vue.config.productionTip = false;

// Router
const routerInstance = router.get();
const storeInstance = storeFactory.get();

// --- Web Title Sync Patch ---

// 1. Watch Vuex for model title changes
storeInstance.watch(
(state) => state.threatmodel?.data?.summary?.title,
(newTitle) => {
if (newTitle) {
setPageTitle(newTitle);
}
}
);

// 2. Reset title when returning home/dashboard
routerInstance.afterEach((to) => {
if (to.name === 'HomePage' || to.name === 'MainDashboard') {
setPageTitle();
}
});

new Vue({
router: router.get(),
store: storeFactory.get(),
Expand Down
7 changes: 6 additions & 1 deletion td.vue/src/views/MainDashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { mapState } from 'vuex';

import TdDashboardAction from '@/components/DashboardAction.vue';
import { getDashboardActions } from '@/service/provider/providers.js';
import { setPageTitle } from '../utils/title';

export default {
name: 'MainDashboard',
Expand All @@ -47,6 +48,10 @@ export default {
},
computed: mapState({
actions: (state) => getDashboardActions(state.provider.selected)
})
}),
mounted () {
// When Dashboard loads, reset to default title
setPageTitle();
}
};
</script>
7 changes: 7 additions & 0 deletions td.vue/src/views/ThreatModel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ export default {
this.$store.dispatch(tmActions.update, update);
// if a diagram has just been closed, the history insists on marking the model as modified
this.$store.dispatch(tmActions.notModified);


if (this.model?.summary?.title && window.electronAPI) {
console.log('RENDERER: Setting title from ThreatModel view:', this.model.summary.title);
window.electronAPI.modelOpened(this.model.summary.title);
}

}
};
</script>
9 changes: 9 additions & 0 deletions td.vue/src/views/ThreatModelEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ export default {
}
// stop the save button from leaving the threat model edit view
// this.$router.push({ name: `${this.providerType}ThreatModel`, params: this.$route.params });

try {
if (window.electronAPI && this.model?.summary?.title) {
window.electronAPI.modelOpened(this.model.summary.title);
}
} catch (err) {
console.err('Failed to update title on save: ', err);
}

},
async onReloadClick(evt) {
evt.preventDefault();
Expand Down
Loading