Skip to content

Commit

Permalink
refactor: improve button layout and styling
Browse files Browse the repository at this point in the history
- Move auto-seek button to its own grid item container
- Maintain consistent styling with Coursera's design
- Fix button positioning and layout
- Keep original button styles for perfect match
  • Loading branch information
lumpinif committed Nov 22, 2024
1 parent 64b88b4 commit fdbb571
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 8 deletions.
82 changes: 82 additions & 0 deletions src/content/components/AutoSeekToTranscript.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { useState } from 'react';
import { findElement } from '../utils/domUtils';

export const AutoSeekToTranscript: React.FC = () => {
const [status, setStatus] = useState<'idle' | 'seeking' | 'error'>('idle');

const handleSeekToStart = async () => {
try {
setStatus('seeking');
const transcriptContainer = await findElement([
'[data-track-component="interactive_transcript"]',
'.rc-Transcript[role="presentation"]',
'.rc-Transcript',
]);

if (!transcriptContainer) {
throw new Error('Transcript container not found');
}

const firstParagraph = transcriptContainer.querySelector('.rc-Paragraph');
if (!firstParagraph) {
throw new Error('First paragraph not found');
}

const timestampButton =
firstParagraph.querySelector<HTMLButtonElement>('.timestamp');
if (!timestampButton) {
throw new Error('Timestamp button not found');
}

timestampButton.click();
setStatus('idle');
} catch (error) {
console.error('[AutoSeek] Error:', error);
setStatus('error');
setTimeout(() => setStatus('idle'), 2000);
}
};

return (
<button
className={`cds-105 cds-button-disableElevation cds-button-ghost css-l0otf2 min-h-[48px] overflow-visible text-left align-middle px-2 py-3 opacity-100 font-[var(--cds-font-weight-600)] tracking-[var(--cds-letter-spacing-100)] whitespace-normal outline-none focus:outline-none focus:ring-0 transition-colors duration-200 ease-out rounded-md
${status === 'idle' ? 'hover:text-[var(--cds-color-interactive-primary-hover)] hover:bg-[var(--cds-color-interactive-background-primary-hover-weak)]' : ''}
${status === 'seeking' ? 'text-[var(--cds-color-neutral-disabled)] cursor-not-allowed' : ''}
${status === 'error' ? 'text-[var(--cds-color-feedback-error)]' : ''}`}
onClick={handleSeekToStart}
disabled={status === 'seeking'}
type="button"
>
<span className="cds-button-label">
<span className="cds-button-startIcon">
<svg
aria-hidden="true"
fill="none"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
className="css-0"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2ZM3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10Z"
fill="currentColor"
/>
<path
d="M10 6V10L13 12"
stroke="currentColor"
strokeWidth="1"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
{status === 'idle' && 'Start from Beginning'}
{status === 'seeking' && 'Seeking...'}
{status === 'error' && 'Failed to seek'}
</span>
</button>
);
};
66 changes: 58 additions & 8 deletions src/content/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { CopyTranscriptButton } from './components/CopyTranscriptButton';
import { AutoSeekToTranscript } from './components/AutoSeekToTranscript';
import { findElement } from './utils/domUtils';
import { shouldInitialize } from './utils/urlUtils';
import './styles.css';
Expand All @@ -11,7 +12,7 @@ const TABLIST_SELECTORS = [
'[aria-label="Related lecture content tabs"][role="tablist"]',
];

const App = () => {
const App: React.FC = () => {
const [isTranscriptSelected, setIsTranscriptSelected] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -50,7 +51,7 @@ const init = async () => {
return;
}

// Check if button already exists
// Initialize copy transcript button
const existingButton = document.getElementById('coursera-copilot-button');
if (existingButton) {
console.log(
Expand All @@ -77,25 +78,74 @@ const init = async () => {
} else {
console.warn('[Coursera Copilot] Could not find tablist');
}

// Initialize auto seek button
const existingSeekButton = document.getElementById(
'auto-seek-button-container'
);
if (existingSeekButton) return;

const titleContainer = await findElement(['#video-item-title-and-save-note']);
if (titleContainer) {
// Create a new grid item container for our button
const seekGridItem = document.createElement('div');
seekGridItem.style.display = 'flex';
seekGridItem.style.alignItems = 'center';
seekGridItem.style.justifyContent = 'flex-end';
seekGridItem.style.minWidth = '0';

// Create our button container
const seekButtonContainer = document.createElement('div');
seekButtonContainer.id = 'auto-seek-button-container';
seekButtonContainer.className = 'rc-CaptureHighlightButton';

// Add button container to grid item
seekGridItem.appendChild(seekButtonContainer);

// Find title and save note containers
const titleAndAttribution = titleContainer.querySelector(
'div[data-testid="video-item-title-and-attribution"]'
);
const saveNoteGridItem = titleAndAttribution?.nextElementSibling;

if (titleAndAttribution && saveNoteGridItem) {
// Create wrapper for buttons
const buttonWrapper = document.createElement('div');
buttonWrapper.style.display = 'flex';
buttonWrapper.style.gap = '8px';
buttonWrapper.style.alignItems = 'center';
buttonWrapper.style.justifyContent = 'flex-end';
buttonWrapper.style.minWidth = '0';

// Insert our grid item and wrap it with save note button
saveNoteGridItem.insertAdjacentElement('beforebegin', seekGridItem);
seekGridItem.parentNode?.insertBefore(buttonWrapper, seekGridItem);
buttonWrapper.appendChild(seekGridItem);
buttonWrapper.appendChild(saveNoteGridItem);

// Render our button
const seekRoot = createRoot(seekButtonContainer);
seekRoot.render(<AutoSeekToTranscript />);
console.log('[Coursera Copilot] Auto seek button injected successfully');
}
}
};

// Initialize when DOM is ready
if (document.readyState === 'loading') {
console.log(
'[Coursera Copilot] Document loading, waiting for DOMContentLoaded...'
);
document.addEventListener('DOMContentLoaded', () => init());
document.addEventListener('DOMContentLoaded', init);
} else {
console.log('[Coursera Copilot] Document already loaded, initializing...');
init();
}

// Re-initialize on URL changes (for SPA navigation)
let lastUrl = location.href;
// Re-initialize on URL changes
let lastUrl = window.location.href;
new MutationObserver(() => {
const url = location.href;
const url = window.location.href;
if (url !== lastUrl) {
console.log('[Coursera Copilot] URL changed, reinitializing...');
lastUrl = url;
init();
}
Expand Down

0 comments on commit fdbb571

Please sign in to comment.