diff --git a/db/migrate/20251128000001_remove_unique_game_constraint.rb b/db/migrate/20251128000001_remove_unique_game_constraint.rb
index a4715d9..a2638bc 100644
--- a/db/migrate/20251128000001_remove_unique_game_constraint.rb
+++ b/db/migrate/20251128000001_remove_unique_game_constraint.rb
@@ -16,3 +16,4 @@ class RemoveUniqueGameConstraint < ActiveRecord::Migration[7.0]
name: 'index_games_on_player_and_mission_non_unique'
end
end
+
diff --git a/index.html b/index.html
index 1c1291d..0e97b91 100644
--- a/index.html
+++ b/index.html
@@ -76,6 +76,7 @@
Crypto Workstation
+
diff --git a/public/break_escape/css/flag-station-minigame.css b/public/break_escape/css/flag-station-minigame.css
index 68f1b4e..1838e87 100644
--- a/public/break_escape/css/flag-station-minigame.css
+++ b/public/break_escape/css/flag-station-minigame.css
@@ -187,3 +187,4 @@
+
diff --git a/public/break_escape/css/main.css b/public/break_escape/css/main.css
index 745fbe0..6293497 100644
--- a/public/break_escape/css/main.css
+++ b/public/break_escape/css/main.css
@@ -117,6 +117,7 @@ body {
font-family: 'VT323', monospace;
font-size: 18px;
min-height: 25px;
+ position: relative;
}
.title-bar .close-btn {
@@ -138,6 +139,13 @@ body {
background: #c0392b;
}
+/* Center minigame buttons vertically in title-bar */
+.title-bar .minigame-close-button {
+ top: 50%;
+ transform: translateY(-50%);
+ right: 15px;
+}
+
#cyberchef-container {
flex: 1;
width: 100%;
diff --git a/public/break_escape/css/minigames-framework.css b/public/break_escape/css/minigames-framework.css
index afc8774..ed6f1ed 100644
--- a/public/break_escape/css/minigames-framework.css
+++ b/public/break_escape/css/minigames-framework.css
@@ -185,6 +185,35 @@
background: #a93226;
}
+/* Open in new tab button for workstations */
+.minigame-open-new-tab-button {
+ position: absolute;
+ top: 50%;
+ right: 50px;
+ transform: translateY(-50%);
+ width: 30px;
+ height: 30px;
+ background: #3498db;
+ color: white;
+ border: 4px solid #2980b9;
+ cursor: pointer;
+ font-family: 'Press Start 2P', monospace !important;
+ font-size: 18px;
+ z-index: 10000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background-color 0.3s ease;
+}
+
+.minigame-open-new-tab-button:hover {
+ background: #2980b9;
+}
+
+.minigame-open-new-tab-button:active {
+ background: #21618c;
+}
+
/* Progress bar styling for minigames */
.minigame-progress-container {
width: 100%;
diff --git a/public/break_escape/css/vm-launcher-minigame.css b/public/break_escape/css/vm-launcher-minigame.css
index 8bc966b..82f7d5b 100644
--- a/public/break_escape/css/vm-launcher-minigame.css
+++ b/public/break_escape/css/vm-launcher-minigame.css
@@ -193,3 +193,4 @@
+
diff --git a/public/break_escape/js/systems/hacktivity-cable.js b/public/break_escape/js/systems/hacktivity-cable.js
index a9ea4e2..0a60631 100644
--- a/public/break_escape/js/systems/hacktivity-cable.js
+++ b/public/break_escape/js/systems/hacktivity-cable.js
@@ -224,3 +224,4 @@ export default window.hacktivityCable;
+
diff --git a/public/break_escape/js/utils/crypto-workstation.js b/public/break_escape/js/utils/crypto-workstation.js
index 4254a93..686ebc2 100644
--- a/public/break_escape/js/utils/crypto-workstation.js
+++ b/public/break_escape/js/utils/crypto-workstation.js
@@ -44,4 +44,13 @@ export function closeLaptop() {
window.game.input.mouse.enabled = true;
window.game.input.keyboard.enabled = true;
}
+}
+
+// Open the crypto workstation iframe in a new tab
+export function openCryptoWorkstationInNewTab() {
+ const cyberchefFrame = document.getElementById('cyberchef-frame');
+
+ if (cyberchefFrame && cyberchefFrame.src) {
+ window.open(cyberchefFrame.src, '_blank');
+ }
}
\ No newline at end of file
diff --git a/public/break_escape/js/utils/helpers.js b/public/break_escape/js/utils/helpers.js
index 3012e2a..03e42b0 100644
--- a/public/break_escape/js/utils/helpers.js
+++ b/public/break_escape/js/utils/helpers.js
@@ -24,8 +24,8 @@ export function introduceScenario() {
}
// Import crypto workstation functions
-import { createCryptoWorkstation, openCryptoWorkstation, closeLaptop } from './crypto-workstation.js';
-import { createLabWorkstation, openLabWorkstation, closeLabWorkstation } from './lab-workstation.js';
+import { createCryptoWorkstation, openCryptoWorkstation, closeLaptop, openCryptoWorkstationInNewTab } from './crypto-workstation.js';
+import { createLabWorkstation, openLabWorkstation, closeLabWorkstation, openLabWorkstationInNewTab } from './lab-workstation.js';
// Re-export for other modules that import from helpers.js
export { createCryptoWorkstation };
@@ -127,5 +127,7 @@ export function debounce(func, wait) {
// Export functions to global scope for backward compatibility
window.openCryptoWorkstation = openCryptoWorkstation;
window.closeLaptop = closeLaptop;
+window.openCryptoWorkstationInNewTab = openCryptoWorkstationInNewTab;
window.openLabWorkstation = openLabWorkstation;
-window.closeLabWorkstation = closeLabWorkstation;
\ No newline at end of file
+window.closeLabWorkstation = closeLabWorkstation;
+window.openLabWorkstationInNewTab = openLabWorkstationInNewTab;
\ No newline at end of file
diff --git a/public/break_escape/js/utils/lab-workstation.js b/public/break_escape/js/utils/lab-workstation.js
index f33a8ce..b72860a 100644
--- a/public/break_escape/js/utils/lab-workstation.js
+++ b/public/break_escape/js/utils/lab-workstation.js
@@ -54,3 +54,12 @@ export function closeLabWorkstation() {
}
}
+// Open the lab workstation iframe in a new tab
+export function openLabWorkstationInNewTab() {
+ const labFrame = document.getElementById('lab-frame');
+
+ if (labFrame && labFrame.src) {
+ window.open(labFrame.src, '_blank');
+ }
+}
+