@@ -614,12 +614,12 @@ function App() {
size={24}
style={{cursor:'pointer'}}
onClick={() => {
- if (hasMultipleChats) {
- // In multi-chat view, just deselect
+ if (hasChats) {
+ // In chat view with sidebar, just deselect
setSelectedChatId(null);
setMessages([]);
} else {
- // In single chat view, go back to setup
+ // This shouldn't happen, but go back to setup if needed
setFileLoaded(false);
setChats([]);
}
From f2a3c35da968e9843f7b7733ca358859e5623101 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 12:36:03 +0000
Subject: [PATCH 03/16] Improve comment clarity for back button logic
Co-authored-by: empirea9 <85097414+empirea9@users.noreply.github.com>
---
src/App.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/App.js b/src/App.js
index 2a30f12..5a55a5e 100644
--- a/src/App.js
+++ b/src/App.js
@@ -615,11 +615,11 @@ function App() {
style={{cursor:'pointer'}}
onClick={() => {
if (hasChats) {
- // In chat view with sidebar, just deselect
+ // In chat view with sidebar, go back to chat list
setSelectedChatId(null);
setMessages([]);
} else {
- // This shouldn't happen, but go back to setup if needed
+ // Fallback: go back to setup (shouldn't normally happen)
setFileLoaded(false);
setChats([]);
}
From fd84a2f29c4776e3beae16fdbcff9f8ab019c526 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 13:02:39 +0000
Subject: [PATCH 04/16] Fix: Handle inbox folder uploads correctly to detect
all chats
Co-authored-by: empirea9 <85097414+empirea9@users.noreply.github.com>
---
src/App.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/App.js b/src/App.js
index 5a55a5e..0974300 100644
--- a/src/App.js
+++ b/src/App.js
@@ -55,12 +55,14 @@ function App() {
// Helper function to strip Instagram path prefix
const stripInstagramPrefix = (path) => {
if (!path) return path;
- // Remove "your_instagram_activity/messages/inbox/" prefix
+ // Remove "your_instagram_activity/messages/inbox/" prefix or just "inbox/" prefix
const prefixes = [
'your_instagram_activity/messages/inbox/',
'your_instagram_activity\\messages\\inbox\\',
'../your_instagram_activity/messages/inbox/',
- '../your_instagram_activity\\messages\\inbox\\'
+ '../your_instagram_activity\\messages\\inbox\\',
+ 'inbox/',
+ 'inbox\\'
];
let cleanPath = path;
From f4cbe0a1268f5b9712e9e3ea4d450d16f13dcfdf Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 13:34:30 +0000
Subject: [PATCH 05/16] Implement auto-username, reactions, settings modal, and
Instagram link embeds
Co-authored-by: empirea9 <85097414+empirea9@users.noreply.github.com>
---
src/App.css | 261 ++++++++++++++++++++++++++++++++++++++++-
src/App.js | 280 +++++++++++++++++++++++++++++++++++++++++---
src/utils/parser.js | 16 ++-
3 files changed, 537 insertions(+), 20 deletions(-)
diff --git a/src/App.css b/src/App.css
index 619467a..3c9ccbc 100644
--- a/src/App.css
+++ b/src/App.css
@@ -566,4 +566,263 @@ body, html {
color: white;
border: none;
padding: 5px;
-}
\ No newline at end of file
+ border-radius: 4px;
+}
+
+/* Reaction Bubbles */
+.message-reactions {
+ display: flex;
+ gap: 4px;
+ margin-top: 4px;
+ flex-wrap: wrap;
+}
+
+.reaction-bubble {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 12px;
+ padding: 2px 8px;
+ font-size: 14px;
+ display: inline-block;
+}
+
+/* Instagram Link Embed */
+.instagram-embed {
+ margin: 8px 0;
+}
+
+.instagram-link {
+ text-decoration: none;
+ color: inherit;
+ display: block;
+}
+
+.instagram-thumbnail {
+ width: 200px;
+ height: 200px;
+ background: var(--ig-bg-secondary);
+ border-radius: 8px;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: opacity 0.2s;
+}
+
+.instagram-link:hover .instagram-thumbnail {
+ opacity: 0.8;
+}
+
+.instagram-placeholder {
+ text-align: center;
+ color: var(--ig-text-secondary);
+}
+
+.instagram-placeholder div {
+ margin-top: 8px;
+ font-size: 14px;
+}
+
+/* Settings Modal */
+.settings-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.8);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+}
+
+.settings-content {
+ background: var(--ig-bg-primary);
+ border: 1px solid var(--ig-border);
+ border-radius: 12px;
+ width: 90%;
+ max-width: 600px;
+ max-height: 80vh;
+ overflow-y: auto;
+}
+
+.settings-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ border-bottom: 1px solid var(--ig-border);
+}
+
+.settings-header h2 {
+ margin: 0;
+ font-size: 24px;
+}
+
+.settings-body {
+ padding: 20px;
+}
+
+.setting-item {
+ margin-bottom: 24px;
+}
+
+.setting-item label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 600;
+ font-size: 14px;
+ color: var(--ig-text-primary);
+}
+
+.setting-item select,
+.setting-item input {
+ width: 100%;
+ padding: 10px;
+ background: var(--ig-bg-secondary);
+ color: var(--ig-text-primary);
+ border: 1px solid var(--ig-border);
+ border-radius: 8px;
+ font-size: 14px;
+}
+
+.theme-toggle {
+ display: flex;
+ gap: 8px;
+}
+
+.theme-toggle button {
+ flex: 1;
+ padding: 10px;
+ background: var(--ig-bg-secondary);
+ color: var(--ig-text-primary);
+ border: 1px solid var(--ig-border);
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: all 0.2s;
+}
+
+.theme-toggle button.active {
+ background: var(--ig-bubble-mine);
+ border-color: var(--ig-bubble-mine);
+}
+
+.theme-toggle button:hover {
+ background: var(--ig-bg-secondary);
+ opacity: 0.8;
+}
+
+.search-container {
+ display: flex;
+ gap: 8px;
+}
+
+.search-container input {
+ flex: 1;
+}
+
+.search-container button {
+ padding: 10px 16px;
+ background: var(--ig-bubble-mine);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ transition: opacity 0.2s;
+}
+
+.search-container button:hover {
+ opacity: 0.8;
+}
+
+.search-results {
+ margin-top: 16px;
+ max-height: 300px;
+ overflow-y: auto;
+ border: 1px solid var(--ig-border);
+ border-radius: 8px;
+}
+
+.search-result-chat {
+ border-bottom: 1px solid var(--ig-border);
+ padding: 12px;
+}
+
+.search-result-chat:last-child {
+ border-bottom: none;
+}
+
+.search-result-header {
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 6px;
+ margin-bottom: 8px;
+ transition: background 0.2s;
+}
+
+.search-result-header:hover {
+ background: var(--ig-bg-secondary);
+}
+
+.search-result-messages {
+ padding-left: 12px;
+}
+
+.search-result-message {
+ padding: 6px;
+ font-size: 13px;
+ color: var(--ig-text-secondary);
+ border-left: 2px solid var(--ig-border);
+ margin: 4px 0;
+ padding-left: 8px;
+}
+
+.search-result-message .sender {
+ color: var(--ig-bubble-mine);
+ font-weight: 600;
+}
+
+.search-result-more {
+ padding: 6px 8px;
+ font-size: 12px;
+ color: var(--ig-text-secondary);
+ font-style: italic;
+}
+
+.upload-new-btn {
+ width: 100%;
+ padding: 12px;
+ background: var(--ig-bubble-mine);
+ color: white;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ transition: opacity 0.2s;
+}
+
+.upload-new-btn:hover {
+ opacity: 0.8;
+}
+
+/* Light Theme */
+body.light-theme {
+ --ig-bg-primary: #FFFFFF;
+ --ig-bg-secondary: #F0F0F0;
+ --ig-text-primary: #000000;
+ --ig-text-secondary: #737373;
+ --ig-bubble-mine: #3797F0;
+ --ig-bubble-theirs: #EFEFEF;
+ --ig-border: #DBDBDB;
+}
diff --git a/src/App.js b/src/App.js
index 0974300..e6756da 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,5 +1,5 @@
import React, { useState, useRef, useEffect } from 'react';
-import { ArrowLeft, Phone, Video, Info, Maximize2, X } from 'lucide-react';
+import { ArrowLeft, Phone, Video, Info, Maximize2, X, Settings, Search, Upload } from 'lucide-react';
import './App.css';
import { parseInstagramHTML } from './utils/parser';
@@ -23,6 +23,7 @@ function App() {
const [myUsername, setMyUsername] = useState('');
const [fileLoaded, setFileLoaded] = useState(false);
const [timezone, setTimezone] = useState('UTC');
+ const [theme, setTheme] = useState('dark'); // 'dark' or 'light'
const [error, setError] = useState('');
const [mediaFiles, setMediaFiles] = useState({});
const [fullscreenMedia, setFullscreenMedia] = useState(null);
@@ -30,6 +31,9 @@ function App() {
const [allVideos, setAllVideos] = useState([]);
const [isDragging, setIsDragging] = useState(false);
const [folderName, setFolderName] = useState('');
+ const [showSettings, setShowSettings] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [searchResults, setSearchResults] = useState([]);
const chatEndRef = useRef(null);
const videoRefs = useRef({});
const fileInputRef = useRef(null);
@@ -52,6 +56,15 @@ function App() {
};
}, [mediaFiles]);
+ // Apply theme to body
+ useEffect(() => {
+ if (theme === 'light') {
+ document.body.classList.add('light-theme');
+ } else {
+ document.body.classList.remove('light-theme');
+ }
+ }, [theme]);
+
// Helper function to strip Instagram path prefix
const stripInstagramPrefix = (path) => {
if (!path) return path;
@@ -75,6 +88,43 @@ function App() {
return cleanPath;
};
+ // Auto-detect username by finding the most common sender name across all chats
+ const autoDetectUsername = (allChats) => {
+ const senderCounts = {};
+
+ // Count occurrences of each sender across all chats
+ allChats.forEach(chat => {
+ chat.messages.forEach(msg => {
+ if (msg.sender) {
+ senderCounts[msg.sender] = (senderCounts[msg.sender] || 0) + 1;
+ }
+ });
+ });
+
+ // Find the sender who appears in the most chats (not just most messages)
+ const senderInChatCounts = {};
+ allChats.forEach(chat => {
+ const uniqueSenders = new Set(chat.messages.map(m => m.sender).filter(Boolean));
+ uniqueSenders.forEach(sender => {
+ senderInChatCounts[sender] = (senderInChatCounts[sender] || 0) + 1;
+ });
+ });
+
+ // The username is likely the person who appears in ALL or most chats
+ let detectedUsername = '';
+ let maxChatCount = 0;
+
+ Object.keys(senderInChatCounts).forEach(sender => {
+ if (senderInChatCounts[sender] > maxChatCount) {
+ maxChatCount = senderInChatCounts[sender];
+ detectedUsername = sender;
+ }
+ });
+
+ console.log('Auto-detected username:', detectedUsername, 'appears in', maxChatCount, 'chats');
+ return detectedUsername;
+ };
+
// Process entire inbox folder with multiple chat folders
const processInboxFolder = (files) => {
console.log('Processing inbox folder with', files.length, 'files');
@@ -124,6 +174,17 @@ function App() {
if (validChats.length === 0) {
setError('No valid chats found. Please check the folder structure.');
} else {
+ // Auto-detect username
+ const detectedUsername = autoDetectUsername(validChats);
+ setMyUsername(detectedUsername);
+
+ // Re-process messages with detected username to correctly mark "isMine"
+ validChats.forEach(chat => {
+ chat.messages.forEach(msg => {
+ msg.isMine = msg.sender.toLowerCase().includes(detectedUsername.toLowerCase());
+ });
+ });
+
setChats(validChats);
setFileLoaded(true);
setError('');
@@ -344,12 +405,39 @@ function App() {
reader.onload = (event) => {
try {
const htmlContent = event.target.result;
- const parsedMessages = parseInstagramHTML(htmlContent, myUsername, mediaFileMap);
+ // First parse with empty username to get all messages
+ const parsedMessages = parseInstagramHTML(htmlContent, '', mediaFileMap);
if (parsedMessages.length === 0) {
setError('No messages found in the file. Please check the file format.');
setFileLoaded(false);
} else {
+ // Auto-detect username from single chat
+ const senderCounts = {};
+ parsedMessages.forEach(msg => {
+ if (msg.sender) {
+ senderCounts[msg.sender] = (senderCounts[msg.sender] || 0) + 1;
+ }
+ });
+
+ // The username is likely the most frequent sender
+ let detectedUsername = '';
+ let maxCount = 0;
+ Object.keys(senderCounts).forEach(sender => {
+ if (senderCounts[sender] > maxCount) {
+ maxCount = senderCounts[sender];
+ detectedUsername = sender;
+ }
+ });
+
+ setMyUsername(detectedUsername);
+ console.log('Auto-detected username:', detectedUsername);
+
+ // Re-mark messages with correct username
+ parsedMessages.forEach(msg => {
+ msg.isMine = msg.sender.toLowerCase().includes(detectedUsername.toLowerCase());
+ });
+
setMessages(parsedMessages);
setFileLoaded(true);
setError('');
@@ -465,15 +553,7 @@ function App() {
>
Instagram Chat Viewer
-
1. Enter YOUR Username (to identify your messages)
-
setMyUsername(e.target.value)}
- />
-
-
2. Upload folder or files (HTML + images/videos/audio)
+
Upload folder or files (HTML + images/videos/audio)
@@ -663,7 +739,27 @@ function App() {
{msg.sender}
)}
- {msg.content &&
{msg.content}
}
+ {/* Check if message contains Instagram reel/post link */}
+ {(() => {
+ const instagramLinkMatch = msg.content?.match(/(https:\/\/www\.instagram\.com\/(reel|p)\/([a-zA-Z0-9_-]+))/);
+ if (instagramLinkMatch) {
+ const [fullUrl, , type, code] = instagramLinkMatch;
+ // For reels/posts, show thumbnail embed
+ return (
+
+ );
+ }
+ return msg.content &&
{msg.content}
;
+ })()}
{msg.media && msg.media.length > 0 && (
{msg.media.map((media, mediaIdx) => {
@@ -758,6 +854,13 @@ function App() {
{formatTime(msg.timestamp)}
+ {msg.reactions && msg.reactions.length > 0 && (
+
+ {msg.reactions.map((reaction, rIdx) => (
+ {reaction}
+ ))}
+
+ )}
);
@@ -835,6 +938,147 @@ function App() {
)}
)}
+
+ {/* Settings Modal */}
+ {showSettings && (
+