Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions chrome-extension/background.js
Original file line number Diff line number Diff line change
@@ -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' });
}
});
47 changes: 47 additions & 0 deletions chrome-extension/chat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GitHub Chat</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/chat.css">
</head>
<body>
<div class="chat">
<div class="chat-title">
<div class="chat-title-icon">
<svg viewBox="0 0 16 16" width="20" height="20" fill="white">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
</svg>
</div>
<div>
<h1 id="chat-room-name">GitHub Chat</h1>
<h2 id="chat-room-context">Loading...</h2>
</div>
</div>

<div id="name-prompt" class="name-prompt">
<div class="name-prompt-inner">
<h3>Welcome to GitHub Chat</h3>
<p>Enter your name to start chatting</p>
<input type="text" id="username-input" placeholder="Your name..." autofocus>
<button id="username-submit" type="button">Join Chat</button>
</div>
</div>

<div class="messages" style="display:none;">
<div class="messages-content" id="messages-content"></div>
</div>

<div class="message-box" style="display:none;">
<textarea class="message-input" id="message" placeholder="Type a message..."></textarea>
<button type="button" class="message-submit" id="send-btn">Send</button>
</div>
</div>

<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-firestore.js"></script>
<script src="chat.js"></script>
</body>
</html>
183 changes: 183 additions & 0 deletions chrome-extension/chat.js
Original file line number Diff line number Diff line change
@@ -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 += '<span class="message-sender">' + escapeHtml(data.sender) + '</span>';
}
content += '<span class="message-text">' + escapeHtml(data.message) + '</span>';
content += '<span class="message-time">' + formatTime(data.timestamp) + '</span>';

if (isPersonal) {
content += '<button class="btn-delete" data-id="' + docId + '" title="Delete">×</button>';
}

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();
}
});
})();
72 changes: 72 additions & 0 deletions chrome-extension/content.css
Original file line number Diff line number Diff line change
@@ -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);
}
}
66 changes: 66 additions & 0 deletions chrome-extension/content.js
Original file line number Diff line number Diff line change
@@ -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 = `
<svg class="chat-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/>
</svg>
<svg class="close-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
</svg>
`;

// 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);
})();
Loading