1121 lines
30 KiB
JavaScript
1121 lines
30 KiB
JavaScript
import { html, css, LitElement } from "../../assets/lit-core-2.7.4.min.js";
|
|
import { MainView } from "../views/MainView.js";
|
|
import { CustomizeView } from "../views/CustomizeView.js";
|
|
import { HelpView } from "../views/HelpView.js";
|
|
import { HistoryView } from "../views/HistoryView.js";
|
|
import { AssistantView } from "../views/AssistantView.js";
|
|
import { OnboardingView } from "../views/OnboardingView.js";
|
|
import { AICustomizeView } from "../views/AICustomizeView.js";
|
|
import { FeedbackView } from "../views/FeedbackView.js";
|
|
|
|
export class CheatingDaddyApp extends LitElement {
|
|
static styles = css`
|
|
* {
|
|
box-sizing: border-box;
|
|
font-family: var(--font);
|
|
margin: 0;
|
|
padding: 0;
|
|
cursor: default;
|
|
user-select: none;
|
|
}
|
|
|
|
:host {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100vh;
|
|
background: var(--bg-app);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* ── Full app shell: top bar + sidebar/content ── */
|
|
|
|
.app-shell {
|
|
display: flex;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.top-drag-bar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 9999;
|
|
display: flex;
|
|
align-items: center;
|
|
height: 38px;
|
|
background: transparent;
|
|
}
|
|
|
|
.drag-region {
|
|
flex: 1;
|
|
height: 100%;
|
|
-webkit-app-region: drag;
|
|
}
|
|
|
|
.top-drag-bar.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.traffic-lights {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0 var(--space-md);
|
|
height: 100%;
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
|
|
.traffic-light {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.traffic-light:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.traffic-light.close {
|
|
background: #ff5f57;
|
|
}
|
|
|
|
.traffic-light.minimize {
|
|
background: #febc2e;
|
|
}
|
|
|
|
.traffic-light.maximize {
|
|
background: #28c840;
|
|
}
|
|
|
|
.sidebar {
|
|
width: var(--sidebar-width);
|
|
min-width: var(--sidebar-width);
|
|
background: var(--bg-surface);
|
|
border-right: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 42px 0 var(--space-md) 0;
|
|
transition:
|
|
width var(--transition),
|
|
min-width var(--transition),
|
|
opacity var(--transition);
|
|
}
|
|
|
|
.sidebar.hidden {
|
|
width: 0;
|
|
min-width: 0;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
border-right: none;
|
|
opacity: 0;
|
|
}
|
|
|
|
.sidebar-brand {
|
|
padding: var(--space-sm) var(--space-lg);
|
|
padding-top: var(--space-md);
|
|
margin-bottom: var(--space-lg);
|
|
}
|
|
|
|
.sidebar-brand h1 {
|
|
font-size: var(--font-size-sm);
|
|
font-weight: var(--font-weight-semibold);
|
|
color: var(--text-primary);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.sidebar-nav {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
padding: 0 var(--space-sm);
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-radius: var(--radius-md);
|
|
color: var(--text-secondary);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: var(--font-weight-medium);
|
|
cursor: pointer;
|
|
transition:
|
|
color var(--transition),
|
|
background var(--transition);
|
|
border: none;
|
|
background: none;
|
|
width: 100%;
|
|
text-align: left;
|
|
}
|
|
|
|
.nav-item:hover {
|
|
color: var(--text-primary);
|
|
background: var(--bg-hover);
|
|
}
|
|
|
|
.nav-item.active {
|
|
color: var(--text-primary);
|
|
background: var(--bg-elevated);
|
|
}
|
|
|
|
.nav-item svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.sidebar-footer {
|
|
padding: var(--space-sm);
|
|
margin-top: var(--space-sm);
|
|
-webkit-app-region: no-drag;
|
|
}
|
|
|
|
.update-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
width: 100%;
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-radius: var(--radius-md);
|
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
background: rgba(239, 68, 68, 0.08);
|
|
color: var(--danger);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: var(--font-weight-medium);
|
|
cursor: pointer;
|
|
text-align: left;
|
|
transition:
|
|
background var(--transition),
|
|
border-color var(--transition);
|
|
animation: update-wobble 5s ease-in-out infinite;
|
|
}
|
|
|
|
.update-btn:hover {
|
|
background: rgba(239, 68, 68, 0.14);
|
|
border-color: rgba(239, 68, 68, 0.35);
|
|
}
|
|
|
|
@keyframes update-wobble {
|
|
0%,
|
|
90%,
|
|
100% {
|
|
transform: rotate(0deg);
|
|
}
|
|
92% {
|
|
transform: rotate(-2deg);
|
|
}
|
|
94% {
|
|
transform: rotate(2deg);
|
|
}
|
|
96% {
|
|
transform: rotate(-1.5deg);
|
|
}
|
|
98% {
|
|
transform: rotate(1.5deg);
|
|
}
|
|
}
|
|
|
|
.update-btn svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.version-text {
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
padding: var(--space-xs) var(--space-md);
|
|
}
|
|
|
|
/* ── Main content area ── */
|
|
|
|
.content {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--bg-app);
|
|
}
|
|
|
|
/* Live mode top bar */
|
|
.live-bar {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 var(--space-md);
|
|
background: var(--bg-surface);
|
|
border-bottom: 1px solid var(--border);
|
|
height: 36px;
|
|
-webkit-app-region: drag;
|
|
}
|
|
|
|
.live-bar-left {
|
|
display: flex;
|
|
align-items: center;
|
|
-webkit-app-region: no-drag;
|
|
z-index: 1;
|
|
}
|
|
|
|
.live-bar-back {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
background: none;
|
|
border: none;
|
|
padding: var(--space-xs);
|
|
border-radius: var(--radius-sm);
|
|
transition: color var(--transition);
|
|
}
|
|
|
|
.live-bar-back:hover {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.live-bar-back svg {
|
|
width: 14px;
|
|
height: 14px;
|
|
}
|
|
|
|
.live-bar-center {
|
|
position: absolute;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
font-weight: var(--font-weight-medium);
|
|
white-space: nowrap;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.live-bar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-md);
|
|
-webkit-app-region: no-drag;
|
|
z-index: 1;
|
|
}
|
|
|
|
.live-bar-text {
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.live-bar-text.clickable {
|
|
cursor: pointer;
|
|
transition: color var(--transition);
|
|
}
|
|
|
|
.live-bar-text.clickable:hover {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Content inner */
|
|
.content-inner {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.content-inner.live {
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Onboarding fills everything */
|
|
.fullscreen {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 100;
|
|
background: var(--bg-app);
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 6px;
|
|
height: 6px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--border-strong);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #444444;
|
|
}
|
|
`;
|
|
|
|
static properties = {
|
|
currentView: { type: String },
|
|
statusText: { type: String },
|
|
startTime: { type: Number },
|
|
isRecording: { type: Boolean },
|
|
sessionActive: { type: Boolean },
|
|
selectedProfile: { type: String },
|
|
selectedLanguage: { type: String },
|
|
responses: { type: Array },
|
|
currentResponseIndex: { type: Number },
|
|
selectedScreenshotInterval: { type: String },
|
|
selectedImageQuality: { type: String },
|
|
layoutMode: { type: String },
|
|
_viewInstances: { type: Object, state: true },
|
|
_isClickThrough: { state: true },
|
|
_awaitingNewResponse: { state: true },
|
|
shouldAnimateResponse: { type: Boolean },
|
|
_storageLoaded: { state: true },
|
|
_updateAvailable: { state: true },
|
|
_whisperDownloading: { state: true },
|
|
};
|
|
|
|
constructor() {
|
|
super();
|
|
this.currentView = "main";
|
|
this.statusText = "";
|
|
this.startTime = null;
|
|
this.isRecording = false;
|
|
this.sessionActive = false;
|
|
this.selectedProfile = "interview";
|
|
this.selectedLanguage = "en-US";
|
|
this.selectedScreenshotInterval = "5";
|
|
this.selectedImageQuality = "medium";
|
|
this.layoutMode = "normal";
|
|
this.responses = [];
|
|
this.currentResponseIndex = -1;
|
|
this._viewInstances = new Map();
|
|
this._isClickThrough = false;
|
|
this._awaitingNewResponse = false;
|
|
this._currentResponseIsComplete = true;
|
|
this.shouldAnimateResponse = false;
|
|
this._storageLoaded = false;
|
|
this._timerInterval = null;
|
|
this._updateAvailable = false;
|
|
this._whisperDownloading = false;
|
|
this._localVersion = "";
|
|
|
|
this._loadFromStorage();
|
|
this._checkForUpdates();
|
|
}
|
|
|
|
async _checkForUpdates() {
|
|
try {
|
|
this._localVersion = await cheatingDaddy.getVersion();
|
|
this.requestUpdate();
|
|
|
|
const res = await fetch(
|
|
"https://raw.githubusercontent.com/ShiftyX1/Mastermind/refs/heads/master/package.json",
|
|
);
|
|
if (!res.ok) return;
|
|
const remote = await res.json();
|
|
const remoteVersion = remote.version;
|
|
|
|
const toNum = (v) => v.split(".").map(Number);
|
|
const [rMaj, rMin, rPatch] = toNum(remoteVersion);
|
|
const [lMaj, lMin, lPatch] = toNum(this._localVersion);
|
|
|
|
if (
|
|
rMaj > lMaj ||
|
|
(rMaj === lMaj && rMin > lMin) ||
|
|
(rMaj === lMaj && rMin === lMin && rPatch > lPatch)
|
|
) {
|
|
this._updateAvailable = true;
|
|
this.requestUpdate();
|
|
}
|
|
} catch (e) {
|
|
// silently ignore
|
|
}
|
|
}
|
|
|
|
async _loadFromStorage() {
|
|
try {
|
|
const [config, prefs] = await Promise.all([
|
|
cheatingDaddy.storage.getConfig(),
|
|
cheatingDaddy.storage.getPreferences(),
|
|
]);
|
|
|
|
this.currentView = config.onboarded ? "main" : "onboarding";
|
|
this.selectedProfile = prefs.selectedProfile || "interview";
|
|
this.selectedLanguage = prefs.selectedLanguage || "en-US";
|
|
this.selectedScreenshotInterval = prefs.selectedScreenshotInterval || "5";
|
|
this.selectedImageQuality = prefs.selectedImageQuality || "medium";
|
|
this.layoutMode = config.layout || "normal";
|
|
|
|
this._storageLoaded = true;
|
|
this.requestUpdate();
|
|
} catch (error) {
|
|
console.error("Error loading from storage:", error);
|
|
this._storageLoaded = true;
|
|
this.requestUpdate();
|
|
}
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
ipcRenderer.on("new-response", (_, response) =>
|
|
this.addNewResponse(response),
|
|
);
|
|
ipcRenderer.on("update-response", (_, response) =>
|
|
this.updateCurrentResponse(response),
|
|
);
|
|
ipcRenderer.on("update-status", (_, status) => this.setStatus(status));
|
|
ipcRenderer.on("click-through-toggled", (_, isEnabled) => {
|
|
this._isClickThrough = isEnabled;
|
|
});
|
|
ipcRenderer.on("reconnect-failed", (_, data) =>
|
|
this.addNewResponse(data.message),
|
|
);
|
|
ipcRenderer.on("whisper-downloading", (_, downloading) => {
|
|
this._whisperDownloading = downloading;
|
|
});
|
|
}
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
super.disconnectedCallback();
|
|
this._stopTimer();
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
ipcRenderer.removeAllListeners("new-response");
|
|
ipcRenderer.removeAllListeners("update-response");
|
|
ipcRenderer.removeAllListeners("update-status");
|
|
ipcRenderer.removeAllListeners("click-through-toggled");
|
|
ipcRenderer.removeAllListeners("reconnect-failed");
|
|
ipcRenderer.removeAllListeners("whisper-downloading");
|
|
}
|
|
}
|
|
|
|
// ── Timer ──
|
|
|
|
_startTimer() {
|
|
this._stopTimer();
|
|
if (this.startTime) {
|
|
this._timerInterval = setInterval(() => this.requestUpdate(), 1000);
|
|
}
|
|
}
|
|
|
|
_stopTimer() {
|
|
if (this._timerInterval) {
|
|
clearInterval(this._timerInterval);
|
|
this._timerInterval = null;
|
|
}
|
|
}
|
|
|
|
getElapsedTime() {
|
|
if (!this.startTime) return "0:00";
|
|
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
const h = Math.floor(elapsed / 3600);
|
|
const m = Math.floor((elapsed % 3600) / 60);
|
|
const s = elapsed % 60;
|
|
const pad = (n) => String(n).padStart(2, "0");
|
|
if (h > 0) return `${h}:${pad(m)}:${pad(s)}`;
|
|
return `${m}:${pad(s)}`;
|
|
}
|
|
|
|
// ── Status & Responses ──
|
|
|
|
setStatus(text) {
|
|
this.statusText = text;
|
|
if (
|
|
text.includes("Ready") ||
|
|
text.includes("Listening") ||
|
|
text.includes("Error")
|
|
) {
|
|
this._currentResponseIsComplete = true;
|
|
}
|
|
}
|
|
|
|
addNewResponse(response) {
|
|
const wasOnLatest = this.currentResponseIndex === this.responses.length - 1;
|
|
this.responses = [...this.responses, response];
|
|
if (wasOnLatest || this.currentResponseIndex === -1) {
|
|
this.currentResponseIndex = this.responses.length - 1;
|
|
}
|
|
this._awaitingNewResponse = false;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
updateCurrentResponse(response) {
|
|
if (this.responses.length > 0) {
|
|
this.responses = [...this.responses.slice(0, -1), response];
|
|
} else {
|
|
this.addNewResponse(response);
|
|
}
|
|
this.requestUpdate();
|
|
}
|
|
|
|
// ── Navigation ──
|
|
|
|
navigate(view) {
|
|
this.currentView = view;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
async handleClose() {
|
|
if (this.currentView === "assistant") {
|
|
cheatingDaddy.stopCapture();
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("close-session");
|
|
}
|
|
this.sessionActive = false;
|
|
this._stopTimer();
|
|
this.currentView = "main";
|
|
} else {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("quit-application");
|
|
}
|
|
}
|
|
}
|
|
|
|
async _handleMinimize() {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("window-minimize");
|
|
}
|
|
}
|
|
|
|
async handleHideToggle() {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("toggle-window-visibility");
|
|
}
|
|
}
|
|
|
|
// ── Session start ──
|
|
|
|
async handleStart() {
|
|
const prefs = await cheatingDaddy.storage.getPreferences();
|
|
const providerMode = prefs.providerMode || "byok";
|
|
|
|
if (providerMode === "local") {
|
|
const success = await cheatingDaddy.initializeLocal(this.selectedProfile);
|
|
if (!success) {
|
|
const mainView = this.shadowRoot.querySelector("main-view");
|
|
if (mainView && mainView.triggerApiKeyError) {
|
|
mainView.triggerApiKeyError();
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
const apiKey = await cheatingDaddy.storage.getApiKey();
|
|
if (!apiKey || apiKey === "") {
|
|
const mainView = this.shadowRoot.querySelector("main-view");
|
|
if (mainView && mainView.triggerApiKeyError) {
|
|
mainView.triggerApiKeyError();
|
|
}
|
|
return;
|
|
}
|
|
|
|
await cheatingDaddy.initializeGemini(
|
|
this.selectedProfile,
|
|
this.selectedLanguage,
|
|
);
|
|
}
|
|
|
|
cheatingDaddy.startCapture(
|
|
this.selectedScreenshotInterval,
|
|
this.selectedImageQuality,
|
|
);
|
|
this.responses = [];
|
|
this.currentResponseIndex = -1;
|
|
this.startTime = Date.now();
|
|
this.sessionActive = true;
|
|
this.currentView = "assistant";
|
|
this._startTimer();
|
|
}
|
|
|
|
async handleAPIKeyHelp() {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke(
|
|
"open-external",
|
|
"https://cheatingdaddy.com/help/api-key",
|
|
);
|
|
}
|
|
}
|
|
|
|
async handleGroqAPIKeyHelp() {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke(
|
|
"open-external",
|
|
"https://console.groq.com/keys",
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Settings handlers ──
|
|
|
|
async handleProfileChange(profile) {
|
|
this.selectedProfile = profile;
|
|
await cheatingDaddy.storage.updatePreference("selectedProfile", profile);
|
|
}
|
|
|
|
async handleLanguageChange(language) {
|
|
this.selectedLanguage = language;
|
|
await cheatingDaddy.storage.updatePreference("selectedLanguage", language);
|
|
}
|
|
|
|
async handleScreenshotIntervalChange(interval) {
|
|
this.selectedScreenshotInterval = interval;
|
|
await cheatingDaddy.storage.updatePreference(
|
|
"selectedScreenshotInterval",
|
|
interval,
|
|
);
|
|
}
|
|
|
|
async handleImageQualityChange(quality) {
|
|
this.selectedImageQuality = quality;
|
|
await cheatingDaddy.storage.updatePreference(
|
|
"selectedImageQuality",
|
|
quality,
|
|
);
|
|
}
|
|
|
|
async handleLayoutModeChange(layoutMode) {
|
|
this.layoutMode = layoutMode;
|
|
await cheatingDaddy.storage.updateConfig("layout", layoutMode);
|
|
if (window.require) {
|
|
try {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("update-sizes");
|
|
} catch (error) {
|
|
console.error("Failed to update sizes:", error);
|
|
}
|
|
}
|
|
this.requestUpdate();
|
|
}
|
|
|
|
async handleExternalLinkClick(url) {
|
|
if (window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
await ipcRenderer.invoke("open-external", url);
|
|
}
|
|
}
|
|
|
|
async handleSendText(message) {
|
|
const result = await window.cheatingDaddy.sendTextMessage(message);
|
|
if (!result.success) {
|
|
this.setStatus("Error sending message: " + result.error);
|
|
} else {
|
|
this.setStatus("Message sent...");
|
|
this._awaitingNewResponse = true;
|
|
}
|
|
}
|
|
|
|
async handleExpandResponse() {
|
|
const result = await window.cheatingDaddy.expandLastResponse();
|
|
if (!result.success) {
|
|
this.setStatus("Error expanding: " + (result.error || "Unknown error"));
|
|
} else {
|
|
this.setStatus("Expanding response...");
|
|
this._awaitingNewResponse = true;
|
|
}
|
|
}
|
|
|
|
handleResponseIndexChanged(e) {
|
|
this.currentResponseIndex = e.detail.index;
|
|
this.shouldAnimateResponse = false;
|
|
this.requestUpdate();
|
|
}
|
|
|
|
handleOnboardingComplete() {
|
|
this.currentView = "main";
|
|
}
|
|
|
|
updated(changedProperties) {
|
|
super.updated(changedProperties);
|
|
|
|
if (changedProperties.has("currentView") && window.require) {
|
|
const { ipcRenderer } = window.require("electron");
|
|
ipcRenderer.send("view-changed", this.currentView);
|
|
}
|
|
}
|
|
|
|
// ── Helpers ──
|
|
|
|
_isLiveMode() {
|
|
return this.currentView === "assistant";
|
|
}
|
|
|
|
// ── Render ──
|
|
|
|
renderCurrentView() {
|
|
switch (this.currentView) {
|
|
case "onboarding":
|
|
return html`
|
|
<onboarding-view
|
|
.onComplete=${() => this.handleOnboardingComplete()}
|
|
.onClose=${() => this.handleClose()}
|
|
></onboarding-view>
|
|
`;
|
|
|
|
case "main":
|
|
return html`
|
|
<main-view
|
|
.selectedProfile=${this.selectedProfile}
|
|
.onProfileChange=${(p) => this.handleProfileChange(p)}
|
|
.onStart=${() => this.handleStart()}
|
|
.onExternalLink=${(url) => this.handleExternalLinkClick(url)}
|
|
.whisperDownloading=${this._whisperDownloading}
|
|
></main-view>
|
|
`;
|
|
|
|
case "ai-customize":
|
|
return html`
|
|
<ai-customize-view
|
|
.selectedProfile=${this.selectedProfile}
|
|
.onProfileChange=${(p) => this.handleProfileChange(p)}
|
|
></ai-customize-view>
|
|
`;
|
|
|
|
case "customize":
|
|
return html`
|
|
<customize-view
|
|
.selectedProfile=${this.selectedProfile}
|
|
.selectedLanguage=${this.selectedLanguage}
|
|
.selectedScreenshotInterval=${this.selectedScreenshotInterval}
|
|
.selectedImageQuality=${this.selectedImageQuality}
|
|
.layoutMode=${this.layoutMode}
|
|
.onProfileChange=${(p) => this.handleProfileChange(p)}
|
|
.onLanguageChange=${(l) => this.handleLanguageChange(l)}
|
|
.onScreenshotIntervalChange=${(i) =>
|
|
this.handleScreenshotIntervalChange(i)}
|
|
.onImageQualityChange=${(q) => this.handleImageQualityChange(q)}
|
|
.onLayoutModeChange=${(lm) => this.handleLayoutModeChange(lm)}
|
|
></customize-view>
|
|
`;
|
|
|
|
case "feedback":
|
|
return html`<feedback-view></feedback-view>`;
|
|
|
|
case "help":
|
|
return html`<help-view
|
|
.onExternalLinkClick=${(url) => this.handleExternalLinkClick(url)}
|
|
></help-view>`;
|
|
|
|
case "history":
|
|
return html`<history-view></history-view>`;
|
|
|
|
case "assistant":
|
|
return html`
|
|
<assistant-view
|
|
.responses=${this.responses}
|
|
.currentResponseIndex=${this.currentResponseIndex}
|
|
.selectedProfile=${this.selectedProfile}
|
|
.onSendText=${(msg) => this.handleSendText(msg)}
|
|
.onExpandResponse=${() => this.handleExpandResponse()}
|
|
.shouldAnimateResponse=${this.shouldAnimateResponse}
|
|
@response-index-changed=${this.handleResponseIndexChanged}
|
|
@response-animation-complete=${() => {
|
|
this.shouldAnimateResponse = false;
|
|
this._currentResponseIsComplete = true;
|
|
this.requestUpdate();
|
|
}}
|
|
></assistant-view>
|
|
`;
|
|
|
|
default:
|
|
return html`<div>Unknown view: ${this.currentView}</div>`;
|
|
}
|
|
}
|
|
|
|
renderSidebar() {
|
|
const items = [
|
|
{
|
|
id: "main",
|
|
label: "Home",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<g
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="m19 8.71l-5.333-4.148a2.666 2.666 0 0 0-3.274 0L5.059 8.71a2.67 2.67 0 0 0-1.029 2.105v7.2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7.2c0-.823-.38-1.6-1.03-2.105"
|
|
/>
|
|
<path d="M16 15c-2.21 1.333-5.792 1.333-8 0" />
|
|
</g>
|
|
</svg>`,
|
|
},
|
|
{
|
|
id: "ai-customize",
|
|
label: "AI Customization",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M13 3v7h6l-8 11v-7H5z"
|
|
/>
|
|
</svg>`,
|
|
},
|
|
{
|
|
id: "history",
|
|
label: "History",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<g
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M10 20.777a9 9 0 0 1-2.48-.969M14 3.223a9.003 9.003 0 0 1 0 17.554m-9.421-3.684a9 9 0 0 1-1.227-2.592M3.124 10.5c.16-.95.468-1.85.9-2.675l.169-.305m2.714-2.941A9 9 0 0 1 10 3.223"
|
|
/>
|
|
<path d="M12 8v4l3 3" />
|
|
</g>
|
|
</svg>`,
|
|
},
|
|
{
|
|
id: "customize",
|
|
label: "Settings",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<g
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M19.875 6.27A2.23 2.23 0 0 1 21 8.218v7.284c0 .809-.443 1.555-1.158 1.948l-6.75 4.27a2.27 2.27 0 0 1-2.184 0l-6.75-4.27A2.23 2.23 0 0 1 3 15.502V8.217c0-.809.443-1.554 1.158-1.947l6.75-3.98a2.33 2.33 0 0 1 2.25 0l6.75 3.98z"
|
|
/>
|
|
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 1 0-6 0" />
|
|
</g>
|
|
</svg>`,
|
|
},
|
|
{
|
|
id: "feedback",
|
|
label: "Feedback",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<g
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3h-5l-5 3v-3H6a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3zM9.5 9h.01m4.99 0h.01"
|
|
/>
|
|
<path d="M9.5 13a3.5 3.5 0 0 0 5 0" />
|
|
</g>
|
|
</svg>`,
|
|
},
|
|
{
|
|
id: "help",
|
|
label: "Help",
|
|
icon: html`<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<g
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
>
|
|
<path
|
|
d="M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9s-9-1.8-9-9s1.8-9 9-9m0 13v.01"
|
|
/>
|
|
<path d="M12 13a2 2 0 0 0 .914-3.782a1.98 1.98 0 0 0-2.414.483" />
|
|
</g>
|
|
</svg>`,
|
|
},
|
|
];
|
|
|
|
return html`
|
|
<div class="sidebar ${this._isLiveMode() ? "hidden" : ""}">
|
|
<div class="sidebar-brand">
|
|
<h1>Mastermind</h1>
|
|
</div>
|
|
<nav class="sidebar-nav">
|
|
${items.map(
|
|
(item) => html`
|
|
<button
|
|
class="nav-item ${this.currentView === item.id ? "active" : ""}"
|
|
@click=${() => this.navigate(item.id)}
|
|
title=${item.label}
|
|
>
|
|
${item.icon} ${item.label}
|
|
</button>
|
|
`,
|
|
)}
|
|
</nav>
|
|
<div class="sidebar-footer">
|
|
${this._updateAvailable
|
|
? html`
|
|
<button
|
|
class="update-btn"
|
|
@click=${() =>
|
|
this.handleExternalLinkClick(
|
|
"https://cheatingdaddy.com/download",
|
|
)}
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
<path
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M7 11l5 5l5-5m-5-7v12"
|
|
/>
|
|
</svg>
|
|
Update available
|
|
</button>
|
|
`
|
|
: html` <div class="version-text">v${this._localVersion}</div> `}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
renderLiveBar() {
|
|
if (!this._isLiveMode()) return "";
|
|
|
|
const profileLabels = {
|
|
interview: "Interview",
|
|
sales: "Sales Call",
|
|
meeting: "Meeting",
|
|
presentation: "Presentation",
|
|
negotiation: "Negotiation",
|
|
exam: "Exam",
|
|
};
|
|
|
|
return html`
|
|
<div class="live-bar">
|
|
<div class="live-bar-left">
|
|
<button
|
|
class="live-bar-back"
|
|
@click=${() => this.handleClose()}
|
|
title="End session"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 20 20"
|
|
fill="currentColor"
|
|
>
|
|
<path
|
|
fill-rule="evenodd"
|
|
d="M12.79 5.23a.75.75 0 0 1-.02 1.06L8.832 10l3.938 3.71a.75.75 0 1 1-1.04 1.08l-4.5-4.25a.75.75 0 0 1 0-1.08l4.5-4.25a.75.75 0 0 1 1.06.02Z"
|
|
clip-rule="evenodd"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="live-bar-center">
|
|
${profileLabels[this.selectedProfile] || "Session"}
|
|
</div>
|
|
<div class="live-bar-right">
|
|
${this.statusText
|
|
? html`<span class="live-bar-text">${this.statusText}</span>`
|
|
: ""}
|
|
<span class="live-bar-text">${this.getElapsedTime()}</span>
|
|
${this._isClickThrough
|
|
? html`<span class="live-bar-text">[click through]</span>`
|
|
: ""}
|
|
<span
|
|
class="live-bar-text clickable"
|
|
@click=${() => this.handleHideToggle()}
|
|
>[hide]</span
|
|
>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
render() {
|
|
// Onboarding is fullscreen, no sidebar
|
|
if (this.currentView === "onboarding") {
|
|
return html` <div class="fullscreen">${this.renderCurrentView()}</div> `;
|
|
}
|
|
|
|
const isLive = this._isLiveMode();
|
|
|
|
return html`
|
|
<div class="app-shell">
|
|
<div class="top-drag-bar ${isLive ? "hidden" : ""}">
|
|
<div class="traffic-lights">
|
|
<button
|
|
class="traffic-light close"
|
|
@click=${() => this.handleClose()}
|
|
title="Close"
|
|
></button>
|
|
<button
|
|
class="traffic-light minimize"
|
|
@click=${() => this._handleMinimize()}
|
|
title="Minimize"
|
|
></button>
|
|
<button class="traffic-light maximize" title="Maximize"></button>
|
|
</div>
|
|
<div class="drag-region"></div>
|
|
</div>
|
|
${this.renderSidebar()}
|
|
<div class="content">
|
|
${isLive ? this.renderLiveBar() : ""}
|
|
<div class="content-inner ${isLive ? "live" : ""}">
|
|
${this.renderCurrentView()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define("cheating-daddy-app", CheatingDaddyApp);
|