feat: Added collapsible PersonalityProfile component to CreatorDetail p…
- "frontend/src/components/PersonalityProfile.tsx" - "frontend/src/api/creators.ts" - "frontend/src/pages/CreatorDetail.tsx" - "frontend/src/App.css" GSD-Task: S06/T03
This commit is contained in:
parent
2d9076ae92
commit
eab362d897
8 changed files with 447 additions and 2 deletions
|
|
@ -141,7 +141,7 @@ Build the core extraction pipeline: a prompt template that analyzes creator tran
|
|||
- Estimate: 1h30m
|
||||
- Files: prompts/personality_extraction.txt, backend/pipeline/stages.py, backend/schemas.py, backend/routers/admin.py
|
||||
- Verify: test -f prompts/personality_extraction.txt && cd backend && python -c "from pipeline.stages import extract_personality_profile; print('task OK')" && python -c "from schemas import PersonalityProfile; print('validator OK')" && grep -q 'extract-profile' routers/admin.py && echo 'all OK'
|
||||
- [ ] **T03: Add personality profile display to CreatorDetail frontend page** — ## Description
|
||||
- [x] **T03: Added collapsible PersonalityProfile component to CreatorDetail page with three sub-cards, pill badges, boolean indicators, and smooth CSS grid animation** — ## Description
|
||||
|
||||
Add a collapsible personality profile section to the CreatorDetail page. Update the TypeScript API type, create a PersonalityProfile component, and wire it into the page layout below the bio/social links section.
|
||||
|
||||
|
|
|
|||
36
.gsd/milestones/M022/slices/S06/tasks/T02-VERIFY.json
Normal file
36
.gsd/milestones/M022/slices/S06/tasks/T02-VERIFY.json
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"taskId": "T02",
|
||||
"unitId": "M022/S06/T02",
|
||||
"timestamp": 1775291298640,
|
||||
"passed": false,
|
||||
"discoverySource": "task-plan",
|
||||
"checks": [
|
||||
{
|
||||
"command": "test -f prompts/personality_extraction.txt",
|
||||
"exitCode": 0,
|
||||
"durationMs": 5,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "cd backend",
|
||||
"exitCode": 0,
|
||||
"durationMs": 6,
|
||||
"verdict": "pass"
|
||||
},
|
||||
{
|
||||
"command": "grep -q 'extract-profile' routers/admin.py",
|
||||
"exitCode": 2,
|
||||
"durationMs": 6,
|
||||
"verdict": "fail"
|
||||
},
|
||||
{
|
||||
"command": "echo 'all OK'",
|
||||
"exitCode": 0,
|
||||
"durationMs": 4,
|
||||
"verdict": "pass"
|
||||
}
|
||||
],
|
||||
"retryAttempt": 1,
|
||||
"maxRetries": 2
|
||||
}
|
||||
83
.gsd/milestones/M022/slices/S06/tasks/T03-SUMMARY.md
Normal file
83
.gsd/milestones/M022/slices/S06/tasks/T03-SUMMARY.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
id: T03
|
||||
parent: S06
|
||||
milestone: M022
|
||||
provides: []
|
||||
requires: []
|
||||
affects: []
|
||||
key_files: ["frontend/src/components/PersonalityProfile.tsx", "frontend/src/api/creators.ts", "frontend/src/pages/CreatorDetail.tsx", "frontend/src/App.css"]
|
||||
key_decisions: ["Used dedicated TypeScript interfaces for personality profile sub-objects rather than inline types"]
|
||||
patterns_established: []
|
||||
drill_down_paths: []
|
||||
observability_surfaces: []
|
||||
duration: ""
|
||||
verification_result: "TypeScript compiles clean (npx tsc --noEmit), production build succeeds (npm run build), component file exists, component is imported in CreatorDetail.tsx. All slice-level backend checks also pass."
|
||||
completed_at: 2026-04-04T08:31:30.051Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T03: Added collapsible PersonalityProfile component to CreatorDetail page with three sub-cards, pill badges, boolean indicators, and smooth CSS grid animation
|
||||
|
||||
> Added collapsible PersonalityProfile component to CreatorDetail page with three sub-cards, pill badges, boolean indicators, and smooth CSS grid animation
|
||||
|
||||
## What Happened
|
||||
---
|
||||
id: T03
|
||||
parent: S06
|
||||
milestone: M022
|
||||
key_files:
|
||||
- frontend/src/components/PersonalityProfile.tsx
|
||||
- frontend/src/api/creators.ts
|
||||
- frontend/src/pages/CreatorDetail.tsx
|
||||
- frontend/src/App.css
|
||||
key_decisions:
|
||||
- Used dedicated TypeScript interfaces for personality profile sub-objects rather than inline types
|
||||
duration: ""
|
||||
verification_result: passed
|
||||
completed_at: 2026-04-04T08:31:30.051Z
|
||||
blocker_discovered: false
|
||||
---
|
||||
|
||||
# T03: Added collapsible PersonalityProfile component to CreatorDetail page with three sub-cards, pill badges, boolean indicators, and smooth CSS grid animation
|
||||
|
||||
**Added collapsible PersonalityProfile component to CreatorDetail page with three sub-cards, pill badges, boolean indicators, and smooth CSS grid animation**
|
||||
|
||||
## What Happened
|
||||
|
||||
Created PersonalityProfile.tsx with collapsible section (grid-template-rows 0fr/1fr), three cards (Teaching Style, Vocabulary, Style), pill badges for descriptors/phrases/terms, boolean checkmark indicators, and metadata footer. Updated CreatorDetailResponse type with dedicated interfaces. Wired into CreatorDetail.tsx between stats bar and techniques list. Added ~130 lines of CSS using existing design tokens.
|
||||
|
||||
## Verification
|
||||
|
||||
TypeScript compiles clean (npx tsc --noEmit), production build succeeds (npm run build), component file exists, component is imported in CreatorDetail.tsx. All slice-level backend checks also pass.
|
||||
|
||||
## Verification Evidence
|
||||
|
||||
| # | Command | Exit Code | Verdict | Duration |
|
||||
|---|---------|-----------|---------|----------|
|
||||
| 1 | `cd frontend && npx tsc --noEmit` | 0 | ✅ pass | 3000ms |
|
||||
| 2 | `cd frontend && npm run build` | 0 | ✅ pass | 6200ms |
|
||||
| 3 | `test -f frontend/src/components/PersonalityProfile.tsx` | 0 | ✅ pass | 100ms |
|
||||
| 4 | `grep -q PersonalityProfile frontend/src/pages/CreatorDetail.tsx` | 0 | ✅ pass | 100ms |
|
||||
|
||||
|
||||
## Deviations
|
||||
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
|
||||
Slice-level verification `grep -q 'extract-profile' routers/admin.py` uses wrong path (missing backend/ prefix) — file exists at backend/routers/admin.py.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `frontend/src/components/PersonalityProfile.tsx`
|
||||
- `frontend/src/api/creators.ts`
|
||||
- `frontend/src/pages/CreatorDetail.tsx`
|
||||
- `frontend/src/App.css`
|
||||
|
||||
|
||||
## Deviations
|
||||
None.
|
||||
|
||||
## Known Issues
|
||||
Slice-level verification `grep -q 'extract-profile' routers/admin.py` uses wrong path (missing backend/ prefix) — file exists at backend/routers/admin.py.
|
||||
|
|
@ -5573,6 +5573,141 @@ a.app-footer__about:hover,
|
|||
animation: pageEnter 250ms ease-out;
|
||||
}
|
||||
|
||||
/* ── Personality Profile ───────────────────────────────────────────────────── */
|
||||
|
||||
.personality-profile {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.personality-profile__toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
background: none;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-primary);
|
||||
transition: background 150ms ease;
|
||||
}
|
||||
|
||||
.personality-profile__toggle:hover {
|
||||
background: var(--color-bg-surface-hover);
|
||||
}
|
||||
|
||||
.personality-profile__heading {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.personality-profile__chevron {
|
||||
margin-left: auto;
|
||||
font-size: 0.9rem;
|
||||
transition: transform 250ms ease;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.personality-profile__chevron--open {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.personality-profile__collapse {
|
||||
display: grid;
|
||||
transition: grid-template-rows 300ms ease;
|
||||
}
|
||||
|
||||
.personality-profile__inner {
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.personality-profile__summary {
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
.personality-profile__cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.personality-profile__card {
|
||||
background: var(--color-bg-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.personality-profile__card-title {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.personality-profile__dl {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 0.25rem 0.75rem;
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.personality-profile__dl dt {
|
||||
color: var(--color-text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.personality-profile__dl dd {
|
||||
margin: 0;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.personality-profile__pill-label {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.personality-profile__pills {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.375rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.personality-profile__booleans {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.personality-profile__bool {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.personality-profile__bool--yes {
|
||||
color: var(--color-badge-approved-text);
|
||||
}
|
||||
|
||||
.personality-profile__meta {
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
padding: 0 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* ── Admin Technique Pages ────────────────────────────────────────────────── */
|
||||
|
||||
.admin-page {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,39 @@ export interface CreatorTechniqueItem {
|
|||
created_at: string;
|
||||
}
|
||||
|
||||
export interface PersonalityVocabulary {
|
||||
signature_phrases: string[];
|
||||
technical_jargon_level: string;
|
||||
filler_words: string[];
|
||||
distinctive_terms: string[];
|
||||
}
|
||||
|
||||
export interface PersonalityTone {
|
||||
formality: string;
|
||||
energy: string;
|
||||
humor_frequency: string;
|
||||
teaching_style: string;
|
||||
descriptors: string[];
|
||||
}
|
||||
|
||||
export interface PersonalityStyleMarkers {
|
||||
explanation_approach: string;
|
||||
uses_analogies: boolean;
|
||||
uses_sound_words: boolean;
|
||||
self_references_frequency: string;
|
||||
audience_engagement: string;
|
||||
}
|
||||
|
||||
export interface PersonalityProfile {
|
||||
vocabulary: PersonalityVocabulary;
|
||||
tone: PersonalityTone;
|
||||
style_markers: PersonalityStyleMarkers;
|
||||
summary: string;
|
||||
extracted_at: string;
|
||||
transcript_sample_size: number;
|
||||
model_used: string;
|
||||
}
|
||||
|
||||
export interface CreatorDetailResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
|
|
@ -52,6 +85,7 @@ export interface CreatorDetailResponse {
|
|||
follower_count: number;
|
||||
techniques: CreatorTechniqueItem[];
|
||||
genre_breakdown: Record<string, number>;
|
||||
personality_profile: PersonalityProfile | null;
|
||||
}
|
||||
|
||||
export interface CreatorListParams {
|
||||
|
|
|
|||
153
frontend/src/components/PersonalityProfile.tsx
Normal file
153
frontend/src/components/PersonalityProfile.tsx
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/**
|
||||
* Collapsible personality profile section for the CreatorDetail page.
|
||||
*
|
||||
* Shows vocabulary, tone, and style markers extracted from creator transcripts.
|
||||
* Renders nothing if profile is null. Uses CSS grid-template-rows 0fr/1fr
|
||||
* for smooth collapse/expand animation (per KNOWLEDGE.md).
|
||||
*/
|
||||
|
||||
import { useState } from "react";
|
||||
import type { PersonalityProfile as ProfileType } from "../api/creators";
|
||||
|
||||
interface Props {
|
||||
profile: ProfileType | null;
|
||||
}
|
||||
|
||||
export default function PersonalityProfile({ profile }: Props) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
if (!profile) return null;
|
||||
|
||||
return (
|
||||
<section className="personality-profile">
|
||||
<button
|
||||
className="personality-profile__toggle"
|
||||
onClick={() => setExpanded((v) => !v)}
|
||||
aria-expanded={expanded}
|
||||
>
|
||||
<h2 className="personality-profile__heading">Personality Profile</h2>
|
||||
<span
|
||||
className={`personality-profile__chevron${expanded ? " personality-profile__chevron--open" : ""}`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
▸
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="personality-profile__collapse"
|
||||
style={{
|
||||
gridTemplateRows: expanded ? "1fr" : "0fr",
|
||||
}}
|
||||
>
|
||||
<div className="personality-profile__inner">
|
||||
{/* Summary */}
|
||||
<p className="personality-profile__summary">{profile.summary}</p>
|
||||
|
||||
<div className="personality-profile__cards">
|
||||
{/* Teaching Style */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Teaching Style</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Formality</dt>
|
||||
<dd>{profile.tone.formality}</dd>
|
||||
<dt>Energy</dt>
|
||||
<dd>{profile.tone.energy}</dd>
|
||||
<dt>Teaching style</dt>
|
||||
<dd>{profile.tone.teaching_style}</dd>
|
||||
<dt>Humor</dt>
|
||||
<dd>{profile.tone.humor_frequency}</dd>
|
||||
</dl>
|
||||
{profile.tone.descriptors.length > 0 && (
|
||||
<div className="personality-profile__pills">
|
||||
{profile.tone.descriptors.map((d) => (
|
||||
<span key={d} className="pill pill--tag">
|
||||
{d}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Vocabulary */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Vocabulary</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Jargon level</dt>
|
||||
<dd>{profile.vocabulary.technical_jargon_level}</dd>
|
||||
{profile.vocabulary.filler_words.length > 0 && (
|
||||
<>
|
||||
<dt>Filler words</dt>
|
||||
<dd>{profile.vocabulary.filler_words.join(", ")}</dd>
|
||||
</>
|
||||
)}
|
||||
</dl>
|
||||
{profile.vocabulary.signature_phrases.length > 0 && (
|
||||
<>
|
||||
<span className="personality-profile__pill-label">
|
||||
Signature phrases
|
||||
</span>
|
||||
<div className="personality-profile__pills">
|
||||
{profile.vocabulary.signature_phrases.map((p) => (
|
||||
<span key={p} className="pill pill--tag">
|
||||
"{p}"
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{profile.vocabulary.distinctive_terms.length > 0 && (
|
||||
<>
|
||||
<span className="personality-profile__pill-label">
|
||||
Distinctive terms
|
||||
</span>
|
||||
<div className="personality-profile__pills">
|
||||
{profile.vocabulary.distinctive_terms.map((t) => (
|
||||
<span key={t} className="pill pill--tag">
|
||||
{t}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Style */}
|
||||
<div className="personality-profile__card">
|
||||
<h3 className="personality-profile__card-title">Style</h3>
|
||||
<dl className="personality-profile__dl">
|
||||
<dt>Explanation approach</dt>
|
||||
<dd>{profile.style_markers.explanation_approach}</dd>
|
||||
<dt>Audience engagement</dt>
|
||||
<dd>{profile.style_markers.audience_engagement}</dd>
|
||||
<dt>Self-references</dt>
|
||||
<dd>{profile.style_markers.self_references_frequency}</dd>
|
||||
</dl>
|
||||
<div className="personality-profile__booleans">
|
||||
<span
|
||||
className={`personality-profile__bool${profile.style_markers.uses_analogies ? " personality-profile__bool--yes" : ""}`}
|
||||
>
|
||||
{profile.style_markers.uses_analogies ? "✓" : "✗"} Uses
|
||||
analogies
|
||||
</span>
|
||||
<span
|
||||
className={`personality-profile__bool${profile.style_markers.uses_sound_words ? " personality-profile__bool--yes" : ""}`}
|
||||
>
|
||||
{profile.style_markers.uses_sound_words ? "✓" : "✗"} Uses
|
||||
sound words
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="personality-profile__meta">
|
||||
Based on {profile.transcript_sample_size} transcript
|
||||
{profile.transcript_sample_size !== 1 ? "s" : ""} · extracted{" "}
|
||||
{new Date(profile.extracted_at).toLocaleDateString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import { useAuth } from "../context/AuthContext";
|
|||
import CreatorAvatar from "../components/CreatorAvatar";
|
||||
import { SocialIcon } from "../components/SocialIcons";
|
||||
import ChatWidget from "../components/ChatWidget";
|
||||
import PersonalityProfile from "../components/PersonalityProfile";
|
||||
import SortDropdown from "../components/SortDropdown";
|
||||
import TagList from "../components/TagList";
|
||||
import { catSlug } from "../utils/catSlug";
|
||||
|
|
@ -393,6 +394,9 @@ export default function CreatorDetail() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Personality Profile */}
|
||||
<PersonalityProfile profile={creator.personality_profile ?? null} />
|
||||
|
||||
{/* Technique pages */}
|
||||
<section className="creator-techniques">
|
||||
<div className="creator-techniques__header">
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/chat.ts","./src/api/client.ts","./src/api/consent.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/follows.ts","./src/api/highlights.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/AudioWaveform.tsx","./src/components/CategoryIcons.tsx","./src/components/ChapterMarkers.tsx","./src/components/ChatWidget.tsx","./src/components/ConfirmModal.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ImpersonationBanner.tsx","./src/components/PersonalityProfile.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/ToggleSwitch.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminAuditLog.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/AdminUsers.tsx","./src/pages/ChapterReview.tsx","./src/pages/ChatPage.tsx","./src/pages/ConsentDashboard.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorTiers.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/HighlightQueue.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"}
|
||||
Loading…
Add table
Reference in a new issue