diff --git a/chrome-extension/background.js b/chrome-extension/background.js
new file mode 100644
index 0000000..2cdb88b
--- /dev/null
+++ b/chrome-extension/background.js
@@ -0,0 +1,6 @@
+// Toggle chat panel when the extension icon is clicked
+chrome.action.onClicked.addListener((tab) => {
+ if (tab.url && tab.url.startsWith('https://github.com/')) {
+ chrome.tabs.sendMessage(tab.id, { action: 'toggleChat' });
+ }
+});
diff --git a/chrome-extension/chat.html b/chrome-extension/chat.html
new file mode 100644
index 0000000..d371743
--- /dev/null
+++ b/chrome-extension/chat.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+ GitHub Chat
+
+
+
+
+
+
+
+
+
GitHub Chat
+ Loading...
+
+
+
+
+
+
Welcome to GitHub Chat
+
Enter your name to start chatting
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/chrome-extension/chat.js b/chrome-extension/chat.js
new file mode 100644
index 0000000..bd85385
--- /dev/null
+++ b/chrome-extension/chat.js
@@ -0,0 +1,183 @@
+// GitHub Chat - Firebase Chat Logic
+(function () {
+ 'use strict';
+
+ // Firebase configuration (from original repo)
+ var firebaseConfig = {
+ apiKey: "AIzaSyCIW4NPOTP6L2AZDJyZEkd9PkdNLLp8gbA",
+ authDomain: "inquid-chat.firebaseapp.com",
+ databaseURL: "https://inquid-chat-default-rtdb.firebaseio.com",
+ projectId: "inquid-chat",
+ storageBucket: "inquid-chat.appspot.com",
+ messagingSenderId: "771474336667",
+ appId: "1:771474336667:web:819d6a6fe187018f0a0f3e",
+ measurementId: "G-2SRNLJ8J2X"
+ };
+
+ firebase.initializeApp(firebaseConfig);
+ var db = firebase.firestore();
+
+ // Parse URL params for repo context
+ var params = new URLSearchParams(window.location.search);
+ var repoOwner = params.get('owner') || '';
+ var repoName = params.get('repo') || '';
+ var chatRoom = repoOwner && repoName ? repoOwner + '/' + repoName : 'general';
+
+ // UI elements
+ var roomNameEl = document.getElementById('chat-room-name');
+ var roomContextEl = document.getElementById('chat-room-context');
+ var namePrompt = document.getElementById('name-prompt');
+ var usernameInput = document.getElementById('username-input');
+ var usernameSubmit = document.getElementById('username-submit');
+ var messagesContainer = document.querySelector('.messages');
+ var messagesContent = document.getElementById('messages-content');
+ var messageBox = document.querySelector('.message-box');
+ var messageInput = document.getElementById('message');
+ var sendBtn = document.getElementById('send-btn');
+
+ var myName = '';
+ var unsubscribe = null;
+
+ // Set room info in header
+ if (repoOwner && repoName) {
+ roomNameEl.textContent = repoName;
+ roomContextEl.textContent = repoOwner + '/' + repoName;
+ } else {
+ roomNameEl.textContent = 'GitHub Chat';
+ roomContextEl.textContent = 'General Chat Room';
+ }
+
+ // Get Firestore collection for this chat room
+ function getCollection() {
+ return db.collection('rooms').doc(chatRoom.replace('/', '_')).collection('messages');
+ }
+
+ // Format timestamp
+ function formatTime(timestamp) {
+ if (!timestamp) return '';
+ var d = timestamp.toDate ? timestamp.toDate() : new Date(timestamp);
+ var hours = d.getHours().toString().padStart(2, '0');
+ var mins = d.getMinutes().toString().padStart(2, '0');
+ return hours + ':' + mins;
+ }
+
+ // Create a message element
+ function createMessageEl(data, docId) {
+ var div = document.createElement('div');
+ var isPersonal = data.sender === myName;
+ div.className = 'message' + (isPersonal ? ' message-personal' : '') + ' new';
+ div.id = 'message-' + docId;
+
+ var content = '';
+ if (!isPersonal) {
+ content += '' + escapeHtml(data.sender) + '';
+ }
+ content += '' + escapeHtml(data.message) + '';
+ content += '' + formatTime(data.timestamp) + '';
+
+ if (isPersonal) {
+ content += '';
+ }
+
+ div.innerHTML = content;
+
+ // Delete handler
+ var deleteBtn = div.querySelector('.btn-delete');
+ if (deleteBtn) {
+ deleteBtn.addEventListener('click', function () {
+ var id = this.getAttribute('data-id');
+ getCollection().doc(id).delete().then(function () {
+ var el = document.getElementById('message-' + id);
+ if (el) el.remove();
+ });
+ });
+ }
+
+ return div;
+ }
+
+ // Escape HTML to prevent XSS
+ function escapeHtml(text) {
+ var div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ // Scroll to bottom
+ function scrollToBottom() {
+ messagesContent.scrollTop = messagesContent.scrollHeight;
+ }
+
+ // Start listening to messages
+ function startListening() {
+ if (unsubscribe) unsubscribe();
+
+ messagesContent.innerHTML = '';
+
+ unsubscribe = getCollection()
+ .orderBy('timestamp', 'asc')
+ .onSnapshot(function (snapshot) {
+ snapshot.docChanges().forEach(function (change) {
+ if (change.type === 'added') {
+ var el = createMessageEl(change.doc.data(), change.doc.id);
+ messagesContent.appendChild(el);
+ } else if (change.type === 'removed') {
+ var removed = document.getElementById('message-' + change.doc.id);
+ if (removed) removed.remove();
+ }
+ });
+ scrollToBottom();
+ });
+ }
+
+ // Send a message
+ function sendMessage() {
+ var text = messageInput.value.trim();
+ if (!text) return;
+
+ // Clear input immediately for better UX
+ messageInput.value = '';
+ messageInput.focus();
+
+ getCollection().add({
+ message: text,
+ sender: myName,
+ timestamp: firebase.firestore.FieldValue.serverTimestamp(),
+ room: chatRoom
+ }).catch(function (err) {
+ // Restore text if send fails
+ messageInput.value = text;
+ console.error('Failed to send message:', err);
+ });
+ }
+
+ // Join chat with username
+ function joinChat() {
+ var name = usernameInput.value.trim();
+ if (!name) return;
+
+ myName = name;
+ namePrompt.style.display = 'none';
+ messagesContainer.style.display = '';
+ messageBox.style.display = '';
+ messageInput.focus();
+ startListening();
+ }
+
+ // Event listeners
+ usernameSubmit.addEventListener('click', joinChat);
+ usernameInput.addEventListener('keydown', function (e) {
+ if (e.key === 'Enter') {
+ e.preventDefault();
+ joinChat();
+ }
+ });
+
+ sendBtn.addEventListener('click', sendMessage);
+ messageInput.addEventListener('keydown', function (e) {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ sendMessage();
+ }
+ });
+})();
diff --git a/chrome-extension/content.css b/chrome-extension/content.css
new file mode 100644
index 0000000..ed165cc
--- /dev/null
+++ b/chrome-extension/content.css
@@ -0,0 +1,72 @@
+/* GitHub Chat - Toggle Button & Panel */
+#github-chat-toggle {
+ position: fixed;
+ bottom: 24px;
+ right: 24px;
+ width: 56px;
+ height: 56px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #044f48, #2a7561);
+ border: none;
+ cursor: pointer;
+ z-index: 2147483646;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+#github-chat-toggle:hover {
+ transform: scale(1.1);
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
+}
+
+#github-chat-toggle svg {
+ width: 28px;
+ height: 28px;
+ fill: white;
+}
+
+#github-chat-toggle .close-icon {
+ display: none;
+}
+
+#github-chat-toggle.active .chat-icon {
+ display: none;
+}
+
+#github-chat-toggle.active .close-icon {
+ display: block;
+}
+
+#github-chat-panel {
+ position: fixed;
+ bottom: 92px;
+ right: 24px;
+ width: 320px;
+ height: 480px;
+ z-index: 2147483645;
+ border: none;
+ border-radius: 16px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+ overflow: hidden;
+ display: none;
+ transition: opacity 0.2s ease, transform 0.2s ease;
+}
+
+#github-chat-panel.visible {
+ display: block;
+ animation: github-chat-slide-in 0.25s ease-out;
+}
+
+@keyframes github-chat-slide-in {
+ from {
+ opacity: 0;
+ transform: translateY(16px) scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
diff --git a/chrome-extension/content.js b/chrome-extension/content.js
new file mode 100644
index 0000000..a9b716a
--- /dev/null
+++ b/chrome-extension/content.js
@@ -0,0 +1,66 @@
+// GitHub Chat - Content Script
+// Injects a toggle button and chat iframe on github.com pages
+
+(function () {
+ 'use strict';
+
+ // Prevent double injection
+ if (document.getElementById('github-chat-toggle')) return;
+
+ // Extract repo info from current URL
+ function getRepoInfo() {
+ const path = window.location.pathname.split('/').filter(Boolean);
+ if (path.length >= 2) {
+ return { owner: path[0], repo: path[1] };
+ }
+ return null;
+ }
+
+ // Create toggle button
+ const toggle = document.createElement('button');
+ toggle.id = 'github-chat-toggle';
+ toggle.title = 'Toggle GitHub Chat';
+ toggle.innerHTML = `
+
+
+ `;
+
+ // Create iframe for chat
+ const panel = document.createElement('iframe');
+ panel.id = 'github-chat-panel';
+ const repoInfo = getRepoInfo();
+ const chatUrl = chrome.runtime.getURL('chat.html');
+ const params = repoInfo
+ ? `?owner=${encodeURIComponent(repoInfo.owner)}&repo=${encodeURIComponent(repoInfo.repo)}`
+ : '';
+ panel.src = chatUrl + params;
+
+ // Toggle visibility
+ function toggleChat() {
+ const isVisible = panel.classList.contains('visible');
+ if (isVisible) {
+ panel.classList.remove('visible');
+ toggle.classList.remove('active');
+ } else {
+ panel.classList.add('visible');
+ toggle.classList.add('active');
+ }
+ }
+
+ toggle.addEventListener('click', toggleChat);
+
+ // Listen for messages from background script (toolbar icon click)
+ chrome.runtime.onMessage.addListener((message) => {
+ if (message.action === 'toggleChat') {
+ toggleChat();
+ }
+ });
+
+ // Inject into page
+ document.body.appendChild(toggle);
+ document.body.appendChild(panel);
+})();
diff --git a/chrome-extension/css/chat.css b/chrome-extension/css/chat.css
new file mode 100644
index 0000000..423922e
--- /dev/null
+++ b/chrome-extension/css/chat.css
@@ -0,0 +1,267 @@
+/* GitHub Chat Extension - Chat Panel Styles */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+}
+
+body {
+ background: #1a1a2e;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ line-height: 1.4;
+ color: #c9d1d9;
+}
+
+/* Chat Container */
+.chat {
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ overflow: hidden;
+}
+
+/* Chat Title */
+.chat-title {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 12px 16px;
+ background: linear-gradient(135deg, #044f48, #2a7561);
+ color: #fff;
+}
+
+.chat-title-icon {
+ flex-shrink: 0;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255,255,255,0.15);
+ border-radius: 50%;
+}
+
+.chat-title h1 {
+ font-size: 14px;
+ font-weight: 600;
+ margin: 0;
+}
+
+.chat-title h2 {
+ font-size: 11px;
+ font-weight: 400;
+ margin: 0;
+ opacity: 0.7;
+}
+
+/* Name Prompt */
+.name-prompt {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+}
+
+.name-prompt-inner {
+ text-align: center;
+ width: 100%;
+}
+
+.name-prompt h3 {
+ font-size: 16px;
+ color: #e6edf3;
+ margin: 0 0 8px;
+}
+
+.name-prompt p {
+ font-size: 12px;
+ color: #8b949e;
+ margin: 0 0 16px;
+}
+
+.name-prompt input {
+ width: 100%;
+ padding: 10px 14px;
+ border: 1px solid #30363d;
+ border-radius: 8px;
+ background: #0d1117;
+ color: #e6edf3;
+ font-size: 13px;
+ outline: none;
+ margin-bottom: 12px;
+}
+
+.name-prompt input:focus {
+ border-color: #2a7561;
+ box-shadow: 0 0 0 3px rgba(42, 117, 97, 0.3);
+}
+
+.name-prompt button {
+ width: 100%;
+ padding: 10px;
+ border: none;
+ border-radius: 8px;
+ background: linear-gradient(135deg, #044f48, #2a7561);
+ color: white;
+ font-size: 13px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.2s;
+}
+
+.name-prompt button:hover {
+ opacity: 0.9;
+}
+
+/* Messages */
+.messages {
+ flex: 1;
+ overflow: hidden;
+ position: relative;
+}
+
+.messages-content {
+ height: 100%;
+ overflow-y: auto;
+ padding: 12px;
+}
+
+.messages-content::-webkit-scrollbar {
+ width: 6px;
+}
+
+.messages-content::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.messages-content::-webkit-scrollbar-thumb {
+ background: rgba(255,255,255,0.15);
+ border-radius: 3px;
+}
+
+.message {
+ display: flex;
+ flex-direction: column;
+ max-width: 85%;
+ margin-bottom: 8px;
+ padding: 8px 12px;
+ border-radius: 12px 12px 12px 4px;
+ background: #21262d;
+ position: relative;
+ animation: fadeIn 0.2s ease;
+}
+
+.message-sender {
+ font-size: 11px;
+ font-weight: 600;
+ color: #58a6ff;
+ margin-bottom: 2px;
+}
+
+.message-text {
+ word-break: break-word;
+}
+
+.message-time {
+ font-size: 10px;
+ color: #484f58;
+ margin-top: 4px;
+ align-self: flex-end;
+}
+
+.message.message-personal {
+ margin-left: auto;
+ background: linear-gradient(135deg, #044f48, #2a7561);
+ color: #fff;
+ border-radius: 12px 12px 4px 12px;
+}
+
+.message.message-personal .message-time {
+ color: rgba(255,255,255,0.5);
+}
+
+.btn-delete {
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ background: none;
+ border: none;
+ color: rgba(255,255,255,0.3);
+ cursor: pointer;
+ font-size: 16px;
+ line-height: 1;
+ padding: 2px 4px;
+ border-radius: 4px;
+ display: none;
+}
+
+.message.message-personal:hover .btn-delete {
+ display: block;
+}
+
+.btn-delete:hover {
+ color: #f85149;
+ background: rgba(248, 81, 73, 0.15);
+}
+
+/* Message Box */
+.message-box {
+ flex: 0 0 auto;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 12px;
+ background: #161b22;
+ border-top: 1px solid #30363d;
+}
+
+.message-input {
+ flex: 1;
+ background: #0d1117;
+ border: 1px solid #30363d;
+ border-radius: 8px;
+ color: #e6edf3;
+ font-size: 13px;
+ padding: 8px 12px;
+ resize: none;
+ height: 36px;
+ outline: none;
+ font-family: inherit;
+}
+
+.message-input:focus {
+ border-color: #2a7561;
+}
+
+.message-submit {
+ flex-shrink: 0;
+ padding: 8px 16px;
+ border: none;
+ border-radius: 8px;
+ background: linear-gradient(135deg, #044f48, #2a7561);
+ color: white;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.2s;
+}
+
+.message-submit:hover {
+ opacity: 0.85;
+}
+
+/* Animations */
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(4px); }
+ to { opacity: 1; transform: translateY(0); }
+}
diff --git a/chrome-extension/css/normalize.css b/chrome-extension/css/normalize.css
new file mode 100644
index 0000000..c38f248
--- /dev/null
+++ b/chrome-extension/css/normalize.css
@@ -0,0 +1 @@
+/*! normalize.css v4.0.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}template,[hidden]{display:none}a{background-color:transparent}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}button,input,select,textarea{font:inherit;margin:0}optgroup{font-weight:bold}button,input,select{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{cursor:pointer}[disabled]{cursor:default}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button:-moz-focusring,input:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type="checkbox"],[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}
diff --git a/chrome-extension/images/icon128.png b/chrome-extension/images/icon128.png
new file mode 100644
index 0000000..3bc129e
Binary files /dev/null and b/chrome-extension/images/icon128.png differ
diff --git a/chrome-extension/images/icon16.png b/chrome-extension/images/icon16.png
new file mode 100644
index 0000000..0b7563a
Binary files /dev/null and b/chrome-extension/images/icon16.png differ
diff --git a/chrome-extension/images/icon48.png b/chrome-extension/images/icon48.png
new file mode 100644
index 0000000..2688a64
Binary files /dev/null and b/chrome-extension/images/icon48.png differ
diff --git a/chrome-extension/manifest.json b/chrome-extension/manifest.json
new file mode 100644
index 0000000..a9e2484
--- /dev/null
+++ b/chrome-extension/manifest.json
@@ -0,0 +1,34 @@
+{
+ "manifest_version": 3,
+ "name": "GitHub Chat",
+ "description": "Chat with GitHub repository owners directly from any GitHub page",
+ "version": "1.0.0",
+ "icons": {
+ "16": "images/icon16.png",
+ "48": "images/icon48.png",
+ "128": "images/icon128.png"
+ },
+ "content_scripts": [
+ {
+ "matches": ["https://github.com/*"],
+ "js": ["content.js"],
+ "css": ["content.css"]
+ }
+ ],
+ "web_accessible_resources": [
+ {
+ "resources": ["chat.html", "chat.js", "css/*", "images/*"],
+ "matches": ["https://github.com/*"]
+ }
+ ],
+ "action": {
+ "default_icon": {
+ "16": "images/icon16.png",
+ "48": "images/icon48.png"
+ },
+ "default_title": "Toggle GitHub Chat"
+ },
+ "background": {
+ "service_worker": "background.js"
+ }
+}