From c3e8e419d9fbe354d377ea0afca3aec48bad00c4 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 10:48:31 +0100 Subject: [PATCH 01/10] WIP header --- frontend/src/App.css | 39 +--- frontend/src/App.tsx | 185 +---------------- frontend/src/components/Header.tsx | 5 - .../src/components/MainComponent/DemoBody.css | 36 ++++ .../src/components/MainComponent/DemoBody.tsx | 186 ++++++++++++++++++ .../components/MainComponent/DemoHeader.css | 18 ++ .../components/MainComponent/DemoHeader.tsx | 20 ++ 7 files changed, 270 insertions(+), 219 deletions(-) delete mode 100644 frontend/src/components/Header.tsx create mode 100644 frontend/src/components/MainComponent/DemoBody.css create mode 100644 frontend/src/components/MainComponent/DemoBody.tsx create mode 100644 frontend/src/components/MainComponent/DemoHeader.css create mode 100644 frontend/src/components/MainComponent/DemoHeader.tsx diff --git a/frontend/src/App.css b/frontend/src/App.css index 8e32a7f37..ce091ee75 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,49 +1,18 @@ -#main-area { - display: flex; - flex-direction: row; +#app-content { height: 100%; -} - -#centre-area { display: flex; flex-direction: column; - border-color: var(--main-border-colour); - border-width: 0px 1px; - border-style: solid; - position: relative; - height: 100%; - width: 40%; - margin: 0 auto; - text-align: center; } -h1 { - margin: 0; - padding: 20px; +#app-content-body { + height: 50%; + flex-grow: 1; } #root { height: 100%; } -.side-bar { - display: flex; - flex-direction: column; - height: 100%; - width: 30%; - overflow-y: auto; -} - -.side-bar-header { - border-color: var(--main-border-colour); - border-width: 0 0 1px 0; - border-style: solid; - margin: 15px; - margin-bottom: 10px; - padding-bottom: 20px; - text-align: center; -} - .prompt-injection-button { background-color: var(--main-button-inactive-background-colour); border-color: var(--main-border-colour); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 756f7c493..531707fca 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,187 +1,14 @@ -import { useState } from "react"; -import { useEffect } from "react"; import "./App.css"; import "./Theme.css"; -import AttackBox from "./components/AttackBox/AttackBox"; -import ChatBox from "./components/ChatBox/ChatBox"; -import DefenceBox from "./components/DefenceBox/DefenceBox"; -import EmailBox from "./components/EmailBox/EmailBox"; -import PhaseSelectionBox from "./components/PhaseSelectionBox/PhaseSelectionBox"; -import Header from "./components/Header"; -import ModelSelectionBox from "./components/ModelSelectionBox/ModelSelectionBox"; -import ExportPDFLink from "./components/ExportChat/ExportPDFLink"; -import { EmailInfo } from "./models/email"; -import { CHAT_MESSAGE_TYPE, ChatMessage } from "./models/chat"; -import { DefenceInfo } from "./models/defence"; -import { getCompletedPhases } from "./service/phaseService"; -import { clearEmails } from "./service/emailService"; -import { clearChat } from "./service/chatService"; -import { resetActiveDefences } from "./service/defenceService"; -import { PHASES } from "./Phases"; -import { ATTACKS_ALL, ATTACKS_PHASE_1 } from "./Attacks"; -import { DEFENCE_DETAILS_ALL, DEFENCE_DETAILS_PHASE } from "./Defences"; -import { PHASE_NAMES } from "./models/phase"; +import DemoHeader from "./components/MainComponent/DemoHeader"; +import DemoBody from "./components/MainComponent/DemoBody"; function App() { - const [emails, setEmails] = useState([]); - const [messages, setMessages] = useState([]); - const [defencesToShow, setDefencesToShow] = - useState(DEFENCE_DETAILS_ALL); - const [triggeredDefences, setTriggeredDefences] = useState([]); - - // start on sandbox mode - const [currentPhase, setCurrentPhase] = useState( - PHASE_NAMES.SANDBOX - ); - const [numCompletedPhases, setNumCompletedPhases] = useState(0); - - const updateTriggeredDefences = (defenceDetails: string[]) => { - // set the new triggered defences - setTriggeredDefences(defenceDetails); - // add a message to the chat - defenceDetails.forEach((defence) => { - defenceTriggered(defence); - }); - }; - - // methods to modify messages - const addChatMessage = (message: ChatMessage) => { - setMessages((messages: ChatMessage[]) => [...messages, message]); - }; - const addInfoMessage = (message: string) => { - addChatMessage({ - message: message, - type: CHAT_MESSAGE_TYPE.INFO, - isOriginalMessage: true, - }); - }; - - const addDefenceTriggeredMessage = (message: string) => { - addChatMessage({ - message: message, - type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, - isOriginalMessage: true, - }); - }; - const addPhasePreambleMessage = (message: string) => { - addChatMessage({ - message: message, - type: CHAT_MESSAGE_TYPE.PHASE_INFO, - isOriginalMessage: true, - }); - }; - - const clearMessages = () => { - // resetting the current phase will also reset the messages - setNewPhase(currentPhase); - }; - - const clearEmailBox = () => { - setEmails([]); - clearEmails(); - }; - - const setNewPhase = (newPhase: PHASE_NAMES) => { - // reset emails and messages from front and backend - clearChat(); - clearEmailBox(); - // clear frontend messages - setMessages([]); - setCurrentPhase(newPhase); - - resetActiveDefences(); - - // add the preamble to the chat - const preambleMessage = PHASES[newPhase].preamble; - addPhasePreambleMessage(preambleMessage.toLowerCase()); - - // choose appropriate defences to display - newPhase === PHASE_NAMES.PHASE_2 - ? setDefencesToShow(DEFENCE_DETAILS_PHASE) - : setDefencesToShow(DEFENCE_DETAILS_ALL); - }; - - // methods to be called when defences are (de)activated - // this adds an info message to the chat - const defenceActivated = (defenceInfo: DefenceInfo) => { - const infoMessage = `${defenceInfo.name} defence activated`; - addInfoMessage(infoMessage.toLowerCase()); - }; - const defenceDeactivated = (defenceInfo: DefenceInfo) => { - const infoMessage = `${defenceInfo.name} defence deactivated`; - addInfoMessage(infoMessage.toLowerCase()); - }; - - //a add a message to the chat when a defence is triggered - const defenceTriggered = (id: String) => { - const defenceInfo = DEFENCE_DETAILS_ALL.find( - (defence) => defence.id === id - )?.name; - const infoMessage = `${defenceInfo} defence triggered`; - addDefenceTriggeredMessage(infoMessage.toLowerCase()); - }; - - // called on mount - useEffect(() => { - getCompletedPhases().then((numCompletedPhases) => { - setNumCompletedPhases(numCompletedPhases); - }); - setNewPhase(currentPhase); - }, []); - return ( - -
- {/* hide defence box on phases 0 and 1. only allow configuration in sandbox */} - {/* hide defence box on phases 0 and 1 */} - {(currentPhase === PHASE_NAMES.PHASE_2 || - currentPhase === PHASE_NAMES.SANDBOX) && ( - 2 ? true : false} - triggeredDefences={triggeredDefences} - defenceActivated={defenceActivated} - defenceDeactivated={defenceDeactivated} - /> - )} - {/* show reduced set of attacks on phase 1 */} - {currentPhase === PHASE_NAMES.PHASE_1 && ( - - )} - {/* show all attacks on phase 2 and sandbox */} - {(currentPhase === PHASE_NAMES.PHASE_2 || - currentPhase === PHASE_NAMES.SANDBOX) && ( - - )} - - {/* hide model selection box on phases 0 and 1 */} - {currentPhase === PHASE_NAMES.SANDBOX && } -
-
-
- -
-
- - -
-
+
+
+
+
); } diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx deleted file mode 100644 index 23edb9ccf..000000000 --- a/frontend/src/components/Header.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function Header() { - return

prompt injection demo

; -} - -export default Header; diff --git a/frontend/src/components/MainComponent/DemoBody.css b/frontend/src/components/MainComponent/DemoBody.css new file mode 100644 index 000000000..f380c9236 --- /dev/null +++ b/frontend/src/components/MainComponent/DemoBody.css @@ -0,0 +1,36 @@ +#main-area { + display: flex; + flex-direction: row; + height: 100%; +} + +#centre-area { + display: flex; + flex-direction: column; + border-color: var(--main-border-colour); + border-width: 0px 1px; + border-style: solid; + position: relative; + height: 100%; + width: 40%; + margin: 0 auto; + text-align: center; +} + +.side-bar { + display: flex; + flex-direction: column; + height: 100%; + width: 30%; + overflow-y: auto; +} + +.side-bar-header { + border-color: var(--main-border-colour); + border-width: 0 0 1px 0; + border-style: solid; + margin: 15px; + margin-bottom: 10px; + padding-bottom: 20px; + text-align: center; +} \ No newline at end of file diff --git a/frontend/src/components/MainComponent/DemoBody.tsx b/frontend/src/components/MainComponent/DemoBody.tsx new file mode 100644 index 000000000..41c6c3fcb --- /dev/null +++ b/frontend/src/components/MainComponent/DemoBody.tsx @@ -0,0 +1,186 @@ +import { useState } from "react"; +import { useEffect } from "react"; + +import "./DemoBody.css"; +import { ATTACKS_PHASE_1, ATTACKS_ALL } from "../../Attacks"; +import { DEFENCE_DETAILS_ALL, DEFENCE_DETAILS_PHASE } from "../../Defences"; +import { PHASES } from "../../Phases"; +import { ChatMessage, CHAT_MESSAGE_TYPE } from "../../models/chat"; +import { DefenceInfo } from "../../models/defence"; +import { EmailInfo } from "../../models/email"; +import { PHASE_NAMES } from "../../models/phase"; +import { clearChat } from "../../service/chatService"; +import { resetActiveDefences } from "../../service/defenceService"; +import { clearEmails } from "../../service/emailService"; +import { getCompletedPhases } from "../../service/phaseService"; +import AttackBox from "../AttackBox/AttackBox"; +import ChatBox from "../ChatBox/ChatBox"; +import DefenceBox from "../DefenceBox/DefenceBox"; +import EmailBox from "../EmailBox/EmailBox"; +import ExportPDFLink from "../ExportChat/ExportPDFLink"; +import ModelSelectionBox from "../ModelSelectionBox/ModelSelectionBox"; +import PhaseSelectionBox from "../PhaseSelectionBox/PhaseSelectionBox"; + +function DemoBody() { + const [emails, setEmails] = useState([]); + const [messages, setMessages] = useState([]); + const [defencesToShow, setDefencesToShow] = + useState(DEFENCE_DETAILS_ALL); + const [triggeredDefences, setTriggeredDefences] = useState([]); + + // start on sandbox mode + const [currentPhase, setCurrentPhase] = useState( + PHASE_NAMES.SANDBOX + ); + const [numCompletedPhases, setNumCompletedPhases] = useState(0); + + const updateTriggeredDefences = (defenceDetails: string[]) => { + // set the new triggered defences + setTriggeredDefences(defenceDetails); + // add a message to the chat + defenceDetails.forEach((defence) => { + defenceTriggered(defence); + }); + }; + + // methods to modify messages + const addChatMessage = (message: ChatMessage) => { + setMessages((messages: ChatMessage[]) => [...messages, message]); + }; + const addInfoMessage = (message: string) => { + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.INFO, + isOriginalMessage: true, + }); + }; + + const addDefenceTriggeredMessage = (message: string) => { + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, + isOriginalMessage: true, + }); + }; + const addPhasePreambleMessage = (message: string) => { + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.PHASE_INFO, + isOriginalMessage: true, + }); + }; + + const clearMessages = () => { + // resetting the current phase will also reset the messages + setNewPhase(currentPhase); + }; + + const clearEmailBox = () => { + setEmails([]); + clearEmails(); + }; + + const setNewPhase = (newPhase: PHASE_NAMES) => { + // reset emails and messages from front and backend + clearChat(); + clearEmailBox(); + // clear frontend messages + setMessages([]); + setCurrentPhase(newPhase); + + resetActiveDefences(); + + // add the preamble to the chat + const preambleMessage = PHASES[newPhase].preamble; + addPhasePreambleMessage(preambleMessage.toLowerCase()); + + // choose appropriate defences to display + newPhase === PHASE_NAMES.PHASE_2 + ? setDefencesToShow(DEFENCE_DETAILS_PHASE) + : setDefencesToShow(DEFENCE_DETAILS_ALL); + }; + + // methods to be called when defences are (de)activated + // this adds an info message to the chat + const defenceActivated = (defenceInfo: DefenceInfo) => { + const infoMessage = `${defenceInfo.name} defence activated`; + addInfoMessage(infoMessage.toLowerCase()); + }; + const defenceDeactivated = (defenceInfo: DefenceInfo) => { + const infoMessage = `${defenceInfo.name} defence deactivated`; + addInfoMessage(infoMessage.toLowerCase()); + }; + + //a add a message to the chat when a defence is triggered + const defenceTriggered = (id: String) => { + const defenceInfo = DEFENCE_DETAILS_ALL.find( + (defence) => defence.id === id + )?.name; + const infoMessage = `${defenceInfo} defence triggered`; + addDefenceTriggeredMessage(infoMessage.toLowerCase()); + }; + + // called on mount + useEffect(() => { + getCompletedPhases().then((numCompletedPhases) => { + setNumCompletedPhases(numCompletedPhases); + }); + setNewPhase(currentPhase); + }, []); + + return ( + +
+ {/* hide defence box on phases 0 and 1. only allow configuration in sandbox */} + {/* hide defence box on phases 0 and 1 */} + {(currentPhase === PHASE_NAMES.PHASE_2 || + currentPhase === PHASE_NAMES.SANDBOX) && ( + 2 ? true : false} + triggeredDefences={triggeredDefences} + defenceActivated={defenceActivated} + defenceDeactivated={defenceDeactivated} + /> + )} + {/* show reduced set of attacks on phase 1 */} + {currentPhase === PHASE_NAMES.PHASE_1 && ( + + )} + {/* show all attacks on phase 2 and sandbox */} + {(currentPhase === PHASE_NAMES.PHASE_2 || + currentPhase === PHASE_NAMES.SANDBOX) && ( + + )} + + {/* hide model selection box on phases 0 and 1 */} + {currentPhase === PHASE_NAMES.SANDBOX && } +
+
+ +
+
+ + +
+
+ ); +} + +export default DemoBody; diff --git a/frontend/src/components/MainComponent/DemoHeader.css b/frontend/src/components/MainComponent/DemoHeader.css new file mode 100644 index 000000000..79c4dfdb8 --- /dev/null +++ b/frontend/src/components/MainComponent/DemoHeader.css @@ -0,0 +1,18 @@ +#demo-header { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +#demo-header-left { + width: 100%; +} + +#demo-header-middle { + white-space: nowrap; +} + +#demo-header-right { + width: 100%; + text-align: right; +} \ No newline at end of file diff --git a/frontend/src/components/MainComponent/DemoHeader.tsx b/frontend/src/components/MainComponent/DemoHeader.tsx new file mode 100644 index 000000000..1f78fda5a --- /dev/null +++ b/frontend/src/components/MainComponent/DemoHeader.tsx @@ -0,0 +1,20 @@ +import "./DemoHeader.css"; + +function DemoHeader() { + return ( +
+ + Prompt Injection Demo + ICON + + + Phase name + + + Choose phase + +
+ ); +} + +export default DemoHeader; From 85fa7f7f662caa5f25b29500dc1a25fab65223d0 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 11:27:36 +0100 Subject: [PATCH 02/10] WIP: Header spacing --- frontend/src/App.css | 8 ++++++++ .../components/MainComponent/DemoHeader.css | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/frontend/src/App.css b/frontend/src/App.css index ce091ee75..1f4d030fd 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -4,6 +4,14 @@ flex-direction: column; } +#app-content-header { + height: 10%; + border-width: 0px; + border-bottom: 5px; + border-color: var(--main-accent-colour); + border-style: solid; +} + #app-content-body { height: 50%; flex-grow: 1; diff --git a/frontend/src/components/MainComponent/DemoHeader.css b/frontend/src/components/MainComponent/DemoHeader.css index 79c4dfdb8..b26b5f339 100644 --- a/frontend/src/components/MainComponent/DemoHeader.css +++ b/frontend/src/components/MainComponent/DemoHeader.css @@ -1,18 +1,36 @@ #demo-header { display: flex; + align-items: center; flex-direction: row; justify-content: space-between; + height: 100%; } #demo-header-left { width: 100%; + padding-left: 32px; +} + +#demo-header-title { + font-size: 36px; + font-style: italic; + font-weight: 700; + padding-right: 24px; } #demo-header-middle { white-space: nowrap; } +#demo-header-current-phase { + color: var(--main-accent-colour); + font-size: 36px; + font-style: italic; + font-weight: 700; +} + #demo-header-right { width: 100%; text-align: right; + padding-right: 32px; } \ No newline at end of file From f9df30ada6c3e5b709702d7f2d441f7b96d17976 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 12:36:24 +0100 Subject: [PATCH 03/10] Header without icon --- frontend/src/App.css | 8 -- frontend/src/App.tsx | 80 +++++++++++++++++- frontend/src/Phases.ts | 8 +- .../src/components/MainComponent/DemoBody.tsx | 84 ++++++------------- .../components/MainComponent/DemoHeader.css | 11 ++- .../components/MainComponent/DemoHeader.tsx | 30 ++++++- .../PhaseSelectionBox/PhaseSelectionBox.css | 1 - .../PhaseSelectionBox/PhaseSelectionBox.tsx | 1 - 8 files changed, 142 insertions(+), 81 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 1f4d030fd..ce091ee75 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -4,14 +4,6 @@ flex-direction: column; } -#app-content-header { - height: 10%; - border-width: 0px; - border-bottom: 5px; - border-color: var(--main-accent-colour); - border-style: solid; -} - #app-content-body { height: 50%; flex-grow: 1; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 531707fca..946d3c139 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,12 +2,88 @@ import "./App.css"; import "./Theme.css"; import DemoHeader from "./components/MainComponent/DemoHeader"; import DemoBody from "./components/MainComponent/DemoBody"; +import { useState } from "react"; +import { PHASE_NAMES } from "./models/phase"; +import { clearChat } from "./service/chatService"; +import { EmailInfo } from "./models/email"; +import { clearEmails } from "./service/emailService"; +import { CHAT_MESSAGE_TYPE, ChatMessage } from "./models/chat"; +import { resetActiveDefences } from "./service/defenceService"; +import { PHASES } from "./Phases"; +import { DEFENCE_DETAILS_ALL, DEFENCE_DETAILS_PHASE } from "./Defences"; +import { DefenceInfo } from "./models/defence"; function App() { + // start on sandbox mode + const [currentPhase, setCurrentPhase] = useState( + PHASE_NAMES.SANDBOX + ); + const [numCompletedPhases, setNumCompletedPhases] = useState(0); + + const [defencesToShow, setDefencesToShow] = + useState(DEFENCE_DETAILS_ALL); + const [emails, setEmails] = useState([]); + const [messages, setMessages] = useState([]); + + + const addChatMessage = (message: ChatMessage) => { + setMessages((messages: ChatMessage[]) => [...messages, message]); + }; + + const addPhasePreambleMessage = (message: string) => { + addChatMessage({ + message: message, + type: CHAT_MESSAGE_TYPE.PHASE_INFO, + isOriginalMessage: true, + }); + }; + + const clearEmailBox = () => { + setEmails([]); + clearEmails(); + }; + + const setNewPhase = (newPhase: PHASE_NAMES) => { + // reset emails and messages from front and backend + clearChat(); + clearEmailBox(); + // clear frontend messages + setMessages([]); + setCurrentPhase(newPhase); + + resetActiveDefences(); + + // add the preamble to the chat + const preambleMessage = PHASES[newPhase].preamble; + addPhasePreambleMessage(preambleMessage.toLowerCase()); + + // choose appropriate defences to display + newPhase === PHASE_NAMES.PHASE_2 + ? setDefencesToShow(DEFENCE_DETAILS_PHASE) + : setDefencesToShow(DEFENCE_DETAILS_ALL); + }; + return (
-
-
+
+ +
+
+ +
); } diff --git a/frontend/src/Phases.ts b/frontend/src/Phases.ts index 82030c32f..686edb1a4 100644 --- a/frontend/src/Phases.ts +++ b/frontend/src/Phases.ts @@ -3,7 +3,7 @@ import { Phase, PHASE_NAMES } from "./models/phase"; const PHASES: Phase[] = [ { id: PHASE_NAMES.PHASE_0, - name: "phase 0", + name: "Phase 0", preamble: "The chatbot can answer some questions about the company and ongoing projects. " + "Your first task is to ask for the name of the secret project, and then email it to bob@scottlogic.com.", @@ -12,7 +12,7 @@ const PHASES: Phase[] = [ }, { id: PHASE_NAMES.PHASE_1, - name: "phase 1", + name: "Phase 1", preamble: "As the secret project was exposed, we have renamed it. " + "You should now try and find out the product owner of the secret project. " + @@ -24,7 +24,7 @@ const PHASES: Phase[] = [ }, { id: PHASE_NAMES.PHASE_2, - name: "phase 2", + name: "Phase 2", preamble: "Since you compromised the secret project again, we have had to take drastic action and cut the project. " + "Meanwhile, we have adopted a new unrelated secret project with a different name, brief, cost, team etc. " + @@ -38,7 +38,7 @@ const PHASES: Phase[] = [ }, { id: PHASE_NAMES.SANDBOX, - name: "sandbox", + name: "Sandbox", preamble: "This is a sandbox environment. " + "Experiment with different attacks and defences while you try to get the bot to reveal sensitive information or perform actions it shouldn't. " + diff --git a/frontend/src/components/MainComponent/DemoBody.tsx b/frontend/src/components/MainComponent/DemoBody.tsx index 41c6c3fcb..fe64bc7fb 100644 --- a/frontend/src/components/MainComponent/DemoBody.tsx +++ b/frontend/src/components/MainComponent/DemoBody.tsx @@ -3,15 +3,10 @@ import { useEffect } from "react"; import "./DemoBody.css"; import { ATTACKS_PHASE_1, ATTACKS_ALL } from "../../Attacks"; -import { DEFENCE_DETAILS_ALL, DEFENCE_DETAILS_PHASE } from "../../Defences"; -import { PHASES } from "../../Phases"; +import { DEFENCE_DETAILS_ALL } from "../../Defences"; import { ChatMessage, CHAT_MESSAGE_TYPE } from "../../models/chat"; import { DefenceInfo } from "../../models/defence"; -import { EmailInfo } from "../../models/email"; import { PHASE_NAMES } from "../../models/phase"; -import { clearChat } from "../../service/chatService"; -import { resetActiveDefences } from "../../service/defenceService"; -import { clearEmails } from "../../service/emailService"; import { getCompletedPhases } from "../../service/phaseService"; import AttackBox from "../AttackBox/AttackBox"; import ChatBox from "../ChatBox/ChatBox"; @@ -19,21 +14,31 @@ import DefenceBox from "../DefenceBox/DefenceBox"; import EmailBox from "../EmailBox/EmailBox"; import ExportPDFLink from "../ExportChat/ExportPDFLink"; import ModelSelectionBox from "../ModelSelectionBox/ModelSelectionBox"; -import PhaseSelectionBox from "../PhaseSelectionBox/PhaseSelectionBox"; +import { EmailInfo } from "../../models/email"; -function DemoBody() { - const [emails, setEmails] = useState([]); - const [messages, setMessages] = useState([]); - const [defencesToShow, setDefencesToShow] = - useState(DEFENCE_DETAILS_ALL); +function DemoBody( + { + currentPhase, + defences, + emails, + messages, + setEmails, + setMessages, + setNewPhase, + setNumCompletedPhases, + }: { + currentPhase: PHASE_NAMES; + defences: DefenceInfo[]; + emails: EmailInfo[]; + messages: ChatMessage[]; + setEmails: (emails: EmailInfo[]) => void; + setMessages: (messages: ChatMessage[]) => void; + setNewPhase: (newPhase: PHASE_NAMES) => void; + setNumCompletedPhases: (numCompletedPhases: number) => void; + } +) { const [triggeredDefences, setTriggeredDefences] = useState([]); - // start on sandbox mode - const [currentPhase, setCurrentPhase] = useState( - PHASE_NAMES.SANDBOX - ); - const [numCompletedPhases, setNumCompletedPhases] = useState(0); - const updateTriggeredDefences = (defenceDetails: string[]) => { // set the new triggered defences setTriggeredDefences(defenceDetails); @@ -45,7 +50,7 @@ function DemoBody() { // methods to modify messages const addChatMessage = (message: ChatMessage) => { - setMessages((messages: ChatMessage[]) => [...messages, message]); + setMessages([...messages, message]); }; const addInfoMessage = (message: string) => { addChatMessage({ @@ -62,44 +67,12 @@ function DemoBody() { isOriginalMessage: true, }); }; - const addPhasePreambleMessage = (message: string) => { - addChatMessage({ - message: message, - type: CHAT_MESSAGE_TYPE.PHASE_INFO, - isOriginalMessage: true, - }); - }; const clearMessages = () => { // resetting the current phase will also reset the messages setNewPhase(currentPhase); }; - const clearEmailBox = () => { - setEmails([]); - clearEmails(); - }; - - const setNewPhase = (newPhase: PHASE_NAMES) => { - // reset emails and messages from front and backend - clearChat(); - clearEmailBox(); - // clear frontend messages - setMessages([]); - setCurrentPhase(newPhase); - - resetActiveDefences(); - - // add the preamble to the chat - const preambleMessage = PHASES[newPhase].preamble; - addPhasePreambleMessage(preambleMessage.toLowerCase()); - - // choose appropriate defences to display - newPhase === PHASE_NAMES.PHASE_2 - ? setDefencesToShow(DEFENCE_DETAILS_PHASE) - : setDefencesToShow(DEFENCE_DETAILS_ALL); - }; - // methods to be called when defences are (de)activated // this adds an info message to the chat const defenceActivated = (defenceInfo: DefenceInfo) => { @@ -111,7 +84,7 @@ function DemoBody() { addInfoMessage(infoMessage.toLowerCase()); }; - //a add a message to the chat when a defence is triggered + // add a message to the chat when a defence is triggered const defenceTriggered = (id: String) => { const defenceInfo = DEFENCE_DETAILS_ALL.find( (defence) => defence.id === id @@ -136,7 +109,7 @@ function DemoBody() { {(currentPhase === PHASE_NAMES.PHASE_2 || currentPhase === PHASE_NAMES.SANDBOX) && ( 2 ? true : false} triggeredDefences={triggeredDefences} defenceActivated={defenceActivated} @@ -172,11 +145,6 @@ function DemoBody() { />
-
diff --git a/frontend/src/components/MainComponent/DemoHeader.css b/frontend/src/components/MainComponent/DemoHeader.css index b26b5f339..dbf04da7d 100644 --- a/frontend/src/components/MainComponent/DemoHeader.css +++ b/frontend/src/components/MainComponent/DemoHeader.css @@ -3,12 +3,15 @@ align-items: center; flex-direction: row; justify-content: space-between; - height: 100%; + padding: 20px 32px; + border-width: 0px; + border-bottom: 3px; + border-color: var(--main-accent-colour); + border-style: solid; } #demo-header-left { width: 100%; - padding-left: 32px; } #demo-header-title { @@ -31,6 +34,6 @@ #demo-header-right { width: 100%; - text-align: right; - padding-right: 32px; + display: flex; + justify-content: flex-end; } \ No newline at end of file diff --git a/frontend/src/components/MainComponent/DemoHeader.tsx b/frontend/src/components/MainComponent/DemoHeader.tsx index 1f78fda5a..f9c5f3f07 100644 --- a/frontend/src/components/MainComponent/DemoHeader.tsx +++ b/frontend/src/components/MainComponent/DemoHeader.tsx @@ -1,6 +1,24 @@ +import { PHASES } from "../../Phases"; +import { PHASE_NAMES } from "../../models/phase"; +import PhaseSelectionBox from "../PhaseSelectionBox/PhaseSelectionBox"; import "./DemoHeader.css"; -function DemoHeader() { +function DemoHeader( + { + currentPhase, + numCompletedPhases, + setNewPhase, + }: { + currentPhase: PHASE_NAMES; + numCompletedPhases: number; + setNewPhase: (newPhase: number) => void; + } +) { + function getPhaseName(phase: PHASE_NAMES) { + const phaseName = PHASES.find((p) => p.id === phase)?.name; + return phaseName || ""; + } + return (
@@ -8,10 +26,16 @@ function DemoHeader() { ICON - Phase name + {getPhaseName(currentPhase)} - Choose phase + + +
); diff --git a/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.css b/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.css index 628b6bb07..e08a1f49c 100644 --- a/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.css +++ b/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.css @@ -1,5 +1,4 @@ #phase-selection-box { - padding: 16px 32px; position: relative; display: flex; justify-content: center; diff --git a/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.tsx b/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.tsx index c0cdc0b19..c2f29dfdb 100644 --- a/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.tsx +++ b/frontend/src/components/PhaseSelectionBox/PhaseSelectionBox.tsx @@ -25,7 +25,6 @@ function PhaseSelectionBox( return ( -
phases
{PHASES.map((phase: Phase, index: number) => { return ( From 4f5e5c3002ae546b38d90105b1f4e5483dad35d9 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 12:41:22 +0100 Subject: [PATCH 04/10] Fixed bug where user message would disappear --- frontend/src/App.tsx | 2 +- frontend/src/components/MainComponent/DemoBody.tsx | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 946d3c139..6a3829797 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -78,8 +78,8 @@ function App() { defences={defencesToShow} emails={emails} messages={messages} + addChatMessage={addChatMessage} setEmails={setEmails} - setMessages={setMessages} setNewPhase={setNewPhase} setNumCompletedPhases={setNumCompletedPhases} /> diff --git a/frontend/src/components/MainComponent/DemoBody.tsx b/frontend/src/components/MainComponent/DemoBody.tsx index fe64bc7fb..f5c3a7fba 100644 --- a/frontend/src/components/MainComponent/DemoBody.tsx +++ b/frontend/src/components/MainComponent/DemoBody.tsx @@ -22,8 +22,8 @@ function DemoBody( defences, emails, messages, + addChatMessage, setEmails, - setMessages, setNewPhase, setNumCompletedPhases, }: { @@ -31,8 +31,8 @@ function DemoBody( defences: DefenceInfo[]; emails: EmailInfo[]; messages: ChatMessage[]; + addChatMessage: (message: ChatMessage) => void; setEmails: (emails: EmailInfo[]) => void; - setMessages: (messages: ChatMessage[]) => void; setNewPhase: (newPhase: PHASE_NAMES) => void; setNumCompletedPhases: (numCompletedPhases: number) => void; } @@ -48,10 +48,6 @@ function DemoBody( }); }; - // methods to modify messages - const addChatMessage = (message: ChatMessage) => { - setMessages([...messages, message]); - }; const addInfoMessage = (message: string) => { addChatMessage({ message: message, From 2e78a0eab5b796952928e106f63bb7eb705ec4da Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 14:52:36 +0100 Subject: [PATCH 05/10] WIP: Moving code around --- frontend/src/App.tsx | 50 ++++++++++------- frontend/src/components/ChatBox/ChatBox.tsx | 38 +++++++------ .../src/components/DefenceBox/DefenceBox.tsx | 13 ----- .../src/components/MainComponent/DemoBody.tsx | 56 ++----------------- 4 files changed, 54 insertions(+), 103 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6a3829797..ce67599b9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,7 +2,7 @@ import "./App.css"; import "./Theme.css"; import DemoHeader from "./components/MainComponent/DemoHeader"; import DemoBody from "./components/MainComponent/DemoBody"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { PHASE_NAMES } from "./models/phase"; import { clearChat } from "./service/chatService"; import { EmailInfo } from "./models/email"; @@ -12,12 +12,11 @@ import { resetActiveDefences } from "./service/defenceService"; import { PHASES } from "./Phases"; import { DEFENCE_DETAILS_ALL, DEFENCE_DETAILS_PHASE } from "./Defences"; import { DefenceInfo } from "./models/defence"; +import { getCompletedPhases } from "./service/phaseService"; function App() { // start on sandbox mode - const [currentPhase, setCurrentPhase] = useState( - PHASE_NAMES.SANDBOX - ); + const [currentPhase, setCurrentPhase] = useState(PHASE_NAMES.SANDBOX); const [numCompletedPhases, setNumCompletedPhases] = useState(0); const [defencesToShow, setDefencesToShow] = @@ -25,12 +24,19 @@ function App() { const [emails, setEmails] = useState([]); const [messages, setMessages] = useState([]); + // called on mount + useEffect(() => { + getCompletedPhases().then((numCompletedPhases) => { + setNumCompletedPhases(numCompletedPhases); + }); + setNewPhase(currentPhase); + }, []); - const addChatMessage = (message: ChatMessage) => { + function addChatMessage(message: ChatMessage) { setMessages((messages: ChatMessage[]) => [...messages, message]); }; - const addPhasePreambleMessage = (message: string) => { + function addPhasePreambleMessage (message: string) { addChatMessage({ message: message, type: CHAT_MESSAGE_TYPE.PHASE_INFO, @@ -38,29 +44,33 @@ function App() { }); }; - const clearEmailBox = () => { - setEmails([]); + function resetPhase() { + setNewPhase(currentPhase); + } + + function setNewPhase(newPhase: PHASE_NAMES) { + setCurrentPhase(newPhase); + + // reset remove emails clearEmails(); - }; + // reset local emails + setEmails([]); - const setNewPhase = (newPhase: PHASE_NAMES) => { - // reset emails and messages from front and backend + // reset remove chat clearChat(); - clearEmailBox(); - // clear frontend messages + // reset local chat setMessages([]); - setCurrentPhase(newPhase); + // reset remote defences resetActiveDefences(); - - // add the preamble to the chat - const preambleMessage = PHASES[newPhase].preamble; - addPhasePreambleMessage(preambleMessage.toLowerCase()); - // choose appropriate defences to display newPhase === PHASE_NAMES.PHASE_2 ? setDefencesToShow(DEFENCE_DETAILS_PHASE) : setDefencesToShow(DEFENCE_DETAILS_ALL); + + // add the preamble to the chat + const preambleMessage = PHASES[newPhase].preamble; + addPhasePreambleMessage(preambleMessage.toLowerCase()); }; return ( @@ -79,8 +89,8 @@ function App() { emails={emails} messages={messages} addChatMessage={addChatMessage} + resetPhase={resetPhase} setEmails={setEmails} - setNewPhase={setNewPhase} setNumCompletedPhases={setNumCompletedPhases} />
diff --git a/frontend/src/components/ChatBox/ChatBox.tsx b/frontend/src/components/ChatBox/ChatBox.tsx index 4a209c1a7..7eccf4dff 100644 --- a/frontend/src/components/ChatBox/ChatBox.tsx +++ b/frontend/src/components/ChatBox/ChatBox.tsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import "./ChatBox.css"; import ChatBoxFeed from "./ChatBoxFeed"; -import { clearChat, sendMessage } from "../../service/chatService"; +import { sendMessage } from "../../service/chatService"; import { getSentEmails } from "../../service/emailService"; import { CHAT_MESSAGE_TYPE, @@ -10,25 +10,24 @@ import { } from "../../models/chat"; import { EmailInfo } from "../../models/email"; import { PHASE_NAMES } from "../../models/phase"; +import { DEFENCE_DETAILS_ALL } from "../../Defences"; function ChatBox( this: any, { messages, currentPhase, + addChatMessage, + resetPhase, setNumCompletedPhases, setEmails, - updateTriggeredDefences, - addChatMessage, - clearMessages, }: { messages: ChatMessage[]; currentPhase: PHASE_NAMES; + addChatMessage: (message: ChatMessage) => void; + resetPhase: () => void; setNumCompletedPhases: (numCompletedPhases: number) => void; setEmails: (emails: EmailInfo[]) => void; - updateTriggeredDefences: (defences: string[]) => void; - addChatMessage: (message: ChatMessage) => void; - clearMessages: () => void; } ) { const [isSendingMessage, setIsSendingMessage] = useState(false); @@ -41,13 +40,6 @@ function ChatBox( }); }, [setEmails]); - const clearClicked = () => { - // clear local messages - clearMessages(); - // clear remote messages - clearChat(); - }; - const sendChatMessage = async ( event: React.KeyboardEvent ) => { @@ -84,8 +76,18 @@ function ChatBox( defenceInfo: response.defenceInfo, isOriginalMessage: true, }); - // update triggered defences - updateTriggeredDefences(response.defenceInfo.triggeredDefences); + // add triggered defences to the chat + response.defenceInfo.triggeredDefences.forEach((triggeredDefence) => { + // get user-friendly defence name + const defenceName = DEFENCE_DETAILS_ALL.find((defence) => { + return defence.id === triggeredDefence; + })?.name.toLowerCase(); + addChatMessage({ + type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, + message: `${defenceName} defence triggered`, + isOriginalMessage: true, + }) + }); // we have the message reply setIsSendingMessage(false); @@ -122,7 +124,7 @@ function ChatBox( diff --git a/frontend/src/components/DefenceBox/DefenceBox.tsx b/frontend/src/components/DefenceBox/DefenceBox.tsx index 375643113..bf0d172cd 100644 --- a/frontend/src/components/DefenceBox/DefenceBox.tsx +++ b/frontend/src/components/DefenceBox/DefenceBox.tsx @@ -11,13 +11,11 @@ import { DefenceConfig, DefenceInfo } from "../../models/defence"; function DefenceBox({ defences, - triggeredDefences, showConfigurations, defenceActivated, defenceDeactivated, }: { defences: DefenceInfo[]; - triggeredDefences: string[]; showConfigurations: boolean; defenceActivated: (defenceInfo: DefenceInfo) => void; defenceDeactivated: (defenceInfo: DefenceInfo) => void; @@ -58,17 +56,6 @@ function DefenceBox({ }); }, []); - // update triggered defences - useEffect(() => { - // update state - const newDefences = defenceDetails.map((defenceDetail) => { - defenceDetail.isTriggered = triggeredDefences.includes(defenceDetail.id); - return defenceDetail; - }); - setDefenceDetails(newDefences); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [triggeredDefences]); - const setDefenceActive = (defenceType: string) => { activateDefence(defenceType).then(() => { // update state diff --git a/frontend/src/components/MainComponent/DemoBody.tsx b/frontend/src/components/MainComponent/DemoBody.tsx index f5c3a7fba..9d221b119 100644 --- a/frontend/src/components/MainComponent/DemoBody.tsx +++ b/frontend/src/components/MainComponent/DemoBody.tsx @@ -1,13 +1,8 @@ -import { useState } from "react"; -import { useEffect } from "react"; - import "./DemoBody.css"; import { ATTACKS_PHASE_1, ATTACKS_ALL } from "../../Attacks"; -import { DEFENCE_DETAILS_ALL } from "../../Defences"; import { ChatMessage, CHAT_MESSAGE_TYPE } from "../../models/chat"; import { DefenceInfo } from "../../models/defence"; import { PHASE_NAMES } from "../../models/phase"; -import { getCompletedPhases } from "../../service/phaseService"; import AttackBox from "../AttackBox/AttackBox"; import ChatBox from "../ChatBox/ChatBox"; import DefenceBox from "../DefenceBox/DefenceBox"; @@ -23,8 +18,8 @@ function DemoBody( emails, messages, addChatMessage, + resetPhase, setEmails, - setNewPhase, setNumCompletedPhases, }: { currentPhase: PHASE_NAMES; @@ -32,22 +27,11 @@ function DemoBody( emails: EmailInfo[]; messages: ChatMessage[]; addChatMessage: (message: ChatMessage) => void; + resetPhase: () => void; setEmails: (emails: EmailInfo[]) => void; - setNewPhase: (newPhase: PHASE_NAMES) => void; setNumCompletedPhases: (numCompletedPhases: number) => void; } ) { - const [triggeredDefences, setTriggeredDefences] = useState([]); - - const updateTriggeredDefences = (defenceDetails: string[]) => { - // set the new triggered defences - setTriggeredDefences(defenceDetails); - // add a message to the chat - defenceDetails.forEach((defence) => { - defenceTriggered(defence); - }); - }; - const addInfoMessage = (message: string) => { addChatMessage({ message: message, @@ -56,19 +40,6 @@ function DemoBody( }); }; - const addDefenceTriggeredMessage = (message: string) => { - addChatMessage({ - message: message, - type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, - isOriginalMessage: true, - }); - }; - - const clearMessages = () => { - // resetting the current phase will also reset the messages - setNewPhase(currentPhase); - }; - // methods to be called when defences are (de)activated // this adds an info message to the chat const defenceActivated = (defenceInfo: DefenceInfo) => { @@ -80,23 +51,6 @@ function DemoBody( addInfoMessage(infoMessage.toLowerCase()); }; - // add a message to the chat when a defence is triggered - const defenceTriggered = (id: String) => { - const defenceInfo = DEFENCE_DETAILS_ALL.find( - (defence) => defence.id === id - )?.name; - const infoMessage = `${defenceInfo} defence triggered`; - addDefenceTriggeredMessage(infoMessage.toLowerCase()); - }; - - // called on mount - useEffect(() => { - getCompletedPhases().then((numCompletedPhases) => { - setNumCompletedPhases(numCompletedPhases); - }); - setNewPhase(currentPhase); - }, []); - return (
@@ -107,7 +61,6 @@ function DemoBody( 2 ? true : false} - triggeredDefences={triggeredDefences} defenceActivated={defenceActivated} defenceDeactivated={defenceDeactivated} /> @@ -133,11 +86,10 @@ function DemoBody(
From 8cec215fd35c6a4ebc8d1bd7743440eeed5b9d74 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 14:59:20 +0100 Subject: [PATCH 06/10] Clearing defences on reset --- frontend/src/App.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ce67599b9..a4ebdf719 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -64,9 +64,15 @@ function App() { // reset remote defences resetActiveDefences(); // choose appropriate defences to display - newPhase === PHASE_NAMES.PHASE_2 - ? setDefencesToShow(DEFENCE_DETAILS_PHASE) - : setDefencesToShow(DEFENCE_DETAILS_ALL); + let defences = newPhase === PHASE_NAMES.PHASE_2 + ? DEFENCE_DETAILS_PHASE + : DEFENCE_DETAILS_ALL; + // make all defences inactive + defences = defences.map((defence) => { + defence.isActive = false; + return defence; + }); + setDefencesToShow(defences); // add the preamble to the chat const preambleMessage = PHASES[newPhase].preamble; From 375353c0d30ce0534ce2de305eaad45b3a83a867 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 15:08:40 +0100 Subject: [PATCH 07/10] Only showing triggered defence if it's known --- frontend/src/components/ChatBox/ChatBox.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ChatBox/ChatBox.tsx b/frontend/src/components/ChatBox/ChatBox.tsx index 7eccf4dff..c6839fb65 100644 --- a/frontend/src/components/ChatBox/ChatBox.tsx +++ b/frontend/src/components/ChatBox/ChatBox.tsx @@ -82,11 +82,13 @@ function ChatBox( const defenceName = DEFENCE_DETAILS_ALL.find((defence) => { return defence.id === triggeredDefence; })?.name.toLowerCase(); - addChatMessage({ - type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, - message: `${defenceName} defence triggered`, - isOriginalMessage: true, - }) + if (defenceName) { + addChatMessage({ + type: CHAT_MESSAGE_TYPE.DEFENCE_TRIGGERED, + message: `${defenceName} defence triggered`, + isOriginalMessage: true, + }) + } }); // we have the message reply @@ -126,7 +128,7 @@ function ChatBox( className="prompt-injection-button" onClick={resetPhase.bind(this)} > - clear + reset
From a473313b8ca7719afb350418c499c73c94e5dff1 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 16:43:33 +0100 Subject: [PATCH 08/10] Updated chat footer --- frontend/src/components/ChatBox/ChatBox.css | 17 +++++-- frontend/src/components/ChatBox/ChatBox.tsx | 51 ++++++++++++++------- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/ChatBox/ChatBox.css b/frontend/src/components/ChatBox/ChatBox.css index 1620315de..834567c8a 100644 --- a/frontend/src/components/ChatBox/ChatBox.css +++ b/frontend/src/components/ChatBox/ChatBox.css @@ -5,10 +5,15 @@ } #chat-box-footer { - align-items: center; display: flex; + flex-direction: column; padding: 32px; - height: 40px; +} + +#chat-box-footer-messages { + display: flex; + flex-direction: row; + height: 52px; } #chat-box-input { @@ -21,8 +26,14 @@ box-sizing: border-box; } -#chat-box-button { +#chat-box-button-send { height: 100%; width: 20%; margin-left: 8px; } + +#chat-box-button-reset { + height: 34px; + width: 100%; + margin-top: 10px; +} diff --git a/frontend/src/components/ChatBox/ChatBox.tsx b/frontend/src/components/ChatBox/ChatBox.tsx index c6839fb65..be6d0768b 100644 --- a/frontend/src/components/ChatBox/ChatBox.tsx +++ b/frontend/src/components/ChatBox/ChatBox.tsx @@ -40,13 +40,21 @@ function ChatBox( }); }, [setEmails]); - const sendChatMessage = async ( - event: React.KeyboardEvent - ) => { - if (event.key === "Enter" && event.target !== null && !isSendingMessage) { + function inputKeyPressed(event: React.KeyboardEvent) { + if (event.key === "Enter") { + sendChatMessage(); + } + } + + async function sendChatMessage() { + const inputBoxElement = document.getElementById("chat-box-input") as HTMLInputElement; + // get the message from the input box + const message = inputBoxElement?.value; + // clear the input box + inputBoxElement!.value = ""; + + if (message && !isSendingMessage) { setIsSendingMessage(true); - // get the message - const message = event.currentTarget.value; // if input has been edited, add both messages to the list of messages. otherwise add original message only addChatMessage({ @@ -54,8 +62,6 @@ function ChatBox( type: CHAT_MESSAGE_TYPE.USER, isOriginalMessage: true, }); - // clear the input - event.currentTarget.value = ""; const response: ChatResponse = await sendMessage(message, currentPhase); setNumCompletedPhases(response.numPhasesCompleted); @@ -115,21 +121,32 @@ function ChatBox(
); From 0bdebe3ee2f04c83e280424a0917b0aaad6d9a34 Mon Sep 17 00:00:00 2001 From: George Sproston Date: Fri, 25 Aug 2023 17:28:18 +0100 Subject: [PATCH 09/10] Better chat footer sizings --- frontend/src/components/ChatBox/ChatBox.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/ChatBox/ChatBox.css b/frontend/src/components/ChatBox/ChatBox.css index 834567c8a..eaa260fa7 100644 --- a/frontend/src/components/ChatBox/ChatBox.css +++ b/frontend/src/components/ChatBox/ChatBox.css @@ -7,6 +7,7 @@ #chat-box-footer { display: flex; flex-direction: column; + font-size: 12px; padding: 32px; } @@ -19,16 +20,15 @@ #chat-box-input { align-self: flex-end; justify-content: center; - width: 80%; + flex-grow: 1; height: 100%; padding: 0 12px; - font-size: 16px; box-sizing: border-box; } #chat-box-button-send { height: 100%; - width: 20%; + width: 74px; margin-left: 8px; } From b22219ed149ebe13b3168980ba7d740df5528d3d Mon Sep 17 00:00:00 2001 From: George Sproston Date: Tue, 29 Aug 2023 10:20:06 +0100 Subject: [PATCH 10/10] Chat speech bubbles --- frontend/src/Theme.css | 12 ++- .../src/components/ChatBox/ChatBoxMessage.css | 76 ++++++++++++++++--- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/frontend/src/Theme.css b/frontend/src/Theme.css index 54981102d..1c7d644ff 100644 --- a/frontend/src/Theme.css +++ b/frontend/src/Theme.css @@ -15,10 +15,14 @@ --main-input-background-colour: var(--main-button-inactive-background-colour); --main-input-text-colour: var(--main-button-inactive-text-colour); - --chat-bot-background: linear-gradient(#D482FB, #F3DBFE); - --chat-blocked-background: linear-gradient(var(--main-error-colour), #ffD5DA); - --chat-edited-background: linear-gradient(#6a9b9b, #cae7f5); - --chat-user-background: linear-gradient(var(--main-accent-colour), #8AD5DA); + --chat-bot-colour: #D482FB; + --chat-bot-background: linear-gradient(var(--chat-bot-colour), #F3DBFE); + --chat-blocked-colour: var(--main-error-colour); + --chat-blocked-background: linear-gradient(var(--chat-blocked-colour), #ffacb6); + --chat-edited-colour: #6a9b9b; + --chat-edited-background: linear-gradient(var(--chat-edited-colour), #cae7f5); + --chat-user-colour: var(--main-accent-colour); + --chat-user-background: linear-gradient(var(--chat-user-colour), #8AD5DA); --chat-info-text-colour: #888; --chat-message-text-colour: #000; --chat-triggered-text-color: var(--main-error-colour); diff --git a/frontend/src/components/ChatBox/ChatBoxMessage.css b/frontend/src/components/ChatBox/ChatBoxMessage.css index 387cb127d..2a0715d14 100644 --- a/frontend/src/components/ChatBox/ChatBoxMessage.css +++ b/frontend/src/components/ChatBox/ChatBoxMessage.css @@ -12,33 +12,89 @@ hyphens: auto; white-space: pre-wrap; word-wrap: break-word; + /* For the triangle */ + position: relative; } .chat-box-message-ai { background: var(--chat-bot-background); - float: right; - margin-left: auto; - text-align: right; + border-radius: 0px 8px 8px 8px; + float: left; + margin-right: auto; + text-align: left; +} + +.chat-box-message-ai:before { + content: ""; + position: absolute; + border-style: solid; + /* reduce the damage in FF3.0 */ + display: block; + width: 0; + top: 0px; /* controls vertical position */ + left: -10px; /* value = - border-left-width - border-right-width */ + border-width: 6px 0px 0px 10px; + border-color: var(--chat-bot-colour) transparent; } .chat-box-message-ai-blocked { background: var(--chat-blocked-background); } +.chat-box-message-ai-blocked:before { + content: ""; + position: absolute; + border-style: solid; + /* reduce the damage in FF3.0 */ + display: block; + width: 0; + top: 0px; /* controls vertical position */ + left: -10px; /* value = - border-left-width - border-right-width */ + border-width: 6px 0px 0px 10px; + border-color: var(--chat-blocked-colour) transparent; +} + .chat-box-message-user { background: var(--chat-user-background); - float: left; - margin-right: auto; - text-align: left; + border-radius: 8px 0px 8px 8px; + float: right; + margin-left: auto; + text-align: right; +} + +.chat-box-message-user:after { + content: ""; + position: absolute; + border-style: solid; + /* reduce the damage in FF3.0 */ + display: block; + width: 0; + top: 0px; /* controls vertical position */ + right: -10px; /* value = - border-left-width - border-right-width */ + border-width: 0 0 6px 10px; + border-color: transparent var(--chat-user-colour); } .chat-box-message-user-transformed { background: var(--chat-edited-background); } +.chat-box-message-user-transformed:after { + content: ""; + position: absolute; + border-style: solid; + /* reduce the damage in FF3.0 */ + display: block; + width: 0; + top: 0px; /* controls vertical position */ + right: -10px; /* value = - border-left-width - border-right-width */ + border-width: 0 0 6px 10px; + border-color: transparent var(--chat-edited-colour); +} + .chat-box-message-phase-info { - float: right; - margin-left: auto; - text-align: right; - background-color: rgb(255, 255, 230); + float: right; + margin-left: auto; + text-align: right; + background-color: rgb(255, 255, 230); }