添加AI摘要

main
mula.liu 2025-08-26 21:59:15 +08:00
parent 879c208948
commit 82fd803e6d
11 changed files with 2050 additions and 100 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
<title>会议助手</title>
</head>
<body>
<div id="root"></div>

View File

@ -12,6 +12,7 @@
"dependencies": {
"@uiw/react-md-editor": "^4.0.8",
"axios": "^1.6.2",
"jspdf": "^3.0.2",
"lucide-react": "^0.294.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",

View File

@ -0,0 +1,258 @@
.datetime-picker {
position: relative;
width: 100%;
}
.datetime-display {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border: 1px solid #d1d5db;
border-radius: 8px;
background: white;
cursor: pointer;
transition: all 0.2s ease;
min-height: 48px;
}
.datetime-display:hover {
border-color: #9ca3af;
}
.datetime-display:focus-within,
.datetime-display:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.display-text {
flex: 1;
color: #374151;
font-size: 14px;
}
.display-text.placeholder {
color: #9ca3af;
}
.clear-btn {
background: none;
border: none;
color: #9ca3af;
font-size: 18px;
cursor: pointer;
padding: 4px;
border-radius: 4px;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
}
.clear-btn:hover {
background: #f3f4f6;
color: #6b7280;
}
.datetime-picker-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
z-index: 10;
pointer-events: auto;
}
.datetime-picker-panel {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #e5e7eb;
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
z-index: 20;
margin-top: 4px;
padding: 20px;
min-width: 320px;
max-height: 500px;
height: auto;
min-height: 400px;
overflow-y: auto;
}
.picker-section {
margin-bottom: 24px;
}
.picker-section:last-of-type {
margin-bottom: 16px;
}
.picker-section h4 {
margin: 0 0 12px 0;
color: #374151;
font-size: 14px;
font-weight: 600;
}
.quick-date-options,
.quick-time-options {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
margin-bottom: 16px;
}
.quick-time-options {
grid-template-columns: repeat(4, 1fr);
}
.quick-option {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px 12px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
}
.quick-option:hover {
background: #f1f5f9;
border-color: #cbd5e1;
}
.quick-option.selected {
background: #667eea;
border-color: #667eea;
color: white;
}
.custom-date-input,
.custom-time-input {
display: flex;
align-items: center;
gap: 8px;
}
.custom-time-input {
background: #f8fafc;
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #e2e8f0;
}
.date-input,
.time-input {
border: 1px solid #d1d5db;
border-radius: 6px;
padding: 8px 12px;
font-size: 14px;
background: white;
transition: all 0.2s ease;
flex: 1;
}
.time-input {
border: none;
background: transparent;
flex: 1;
}
.date-input:focus,
.time-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.time-input:focus {
box-shadow: none;
}
.picker-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
padding-top: 16px;
border-top: 1px solid #e5e7eb;
}
.action-btn {
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
}
.action-btn.cancel {
background: #f8fafc;
color: #6b7280;
border-color: #d1d5db;
}
.action-btn.cancel:hover {
background: #f1f5f9;
border-color: #9ca3af;
}
.action-btn.confirm {
background: #667eea;
color: white;
}
.action-btn.confirm:hover {
background: #5a67d8;
}
/* 响应式设计 */
@media (max-width: 768px) {
.datetime-picker-panel {
min-width: 280px;
padding: 16px;
}
.quick-date-options {
grid-template-columns: repeat(2, 1fr);
}
.quick-time-options {
grid-template-columns: repeat(3, 1fr);
}
.picker-actions {
flex-direction: column;
gap: 8px;
}
.action-btn {
width: 100%;
text-align: center;
}
}
/* 改进输入框在Safari中的显示 */
.date-input::-webkit-calendar-picker-indicator,
.time-input::-webkit-calendar-picker-indicator {
background: transparent;
color: #6b7280;
cursor: pointer;
}
.date-input::-webkit-calendar-picker-indicator:hover,
.time-input::-webkit-calendar-picker-indicator:hover {
background: #f3f4f6;
border-radius: 4px;
}

View File

@ -0,0 +1,260 @@
import React, { useState, useEffect } from 'react';
import { Calendar, Clock } from 'lucide-react';
import './DateTimePicker.css';
const DateTimePicker = ({ value, onChange, placeholder = "选择会议时间" }) => {
const [date, setDate] = useState('');
const [time, setTime] = useState('');
const [showQuickSelect, setShowQuickSelect] = useState(false);
const [isInitialized, setIsInitialized] = useState(false);
//
useEffect(() => {
return () => {
setShowQuickSelect(false);
};
}, []);
//
useEffect(() => {
if (value && !isInitialized) {
const dateObj = new Date(value);
if (!isNaN(dateObj.getTime())) {
//
const timeZoneOffset = dateObj.getTimezoneOffset() * 60000;
const localDate = new Date(dateObj.getTime() - timeZoneOffset);
const isoString = localDate.toISOString();
setDate(isoString.split('T')[0]);
setTime(isoString.split('T')[1].slice(0, 5));
}
setIsInitialized(true);
} else if (!value && !isInitialized) {
setDate('');
setTime('');
setIsInitialized(true);
}
}, [value, isInitialized]);
//
useEffect(() => {
// onChange
if (!isInitialized) return;
if (date && time) {
const dateTimeString = `${date}T${time}`;
onChange?.(dateTimeString);
} else if (!date && !time) {
onChange?.('');
}
}, [date, time, isInitialized]); // onChange
//
const timeOptions = [
{ label: '09:00', value: '09:00' },
{ label: '10:00', value: '10:00' },
{ label: '11:00', value: '11:00' },
{ label: '14:00', value: '14:00' },
{ label: '15:00', value: '15:00' },
{ label: '16:00', value: '16:00' },
{ label: '17:00', value: '17:00' },
];
//
const getQuickDateOptions = () => {
const today = new Date();
const options = [];
//
options.push({
label: '今天',
value: today.toISOString().split('T')[0]
});
//
const tomorrow = new Date(today);
tomorrow.setDate(today.getDate() + 1);
options.push({
label: '明天',
value: tomorrow.toISOString().split('T')[0]
});
//
const dayAfterTomorrow = new Date(today);
dayAfterTomorrow.setDate(today.getDate() + 2);
options.push({
label: '后天',
value: dayAfterTomorrow.toISOString().split('T')[0]
});
return options;
};
const quickDateOptions = getQuickDateOptions();
const formatDisplayText = () => {
if (!date && !time) return placeholder;
if (date && time) {
const dateObj = new Date(`${date}T${time}`);
return dateObj.toLocaleString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
if (date) {
const dateObj = new Date(date);
return dateObj.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
return placeholder;
};
const clearDateTime = () => {
setDate('');
setTime('');
//
setIsInitialized(false);
onChange?.('');
};
return (
<div className="datetime-picker">
<div className="datetime-display" onClick={(e) => {
e.stopPropagation();
setShowQuickSelect(!showQuickSelect);
}}>
<Calendar size={18} />
<span className={`display-text ${(!date && !time) ? 'placeholder' : ''}`}>
{formatDisplayText()}
</span>
{(date || time) && (
<button
type="button"
className="clear-btn"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
clearDateTime();
}}
>
×
</button>
)}
</div>
{showQuickSelect && (
<div className="datetime-picker-panel">
<div className="picker-section">
<h4>选择日期</h4>
<div className="quick-date-options">
{quickDateOptions.map((option) => (
<button
key={option.value}
type="button"
className={`quick-option ${date === option.value ? 'selected' : ''}`}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setDate(option.value);
}}
>
{option.label}
</button>
))}
</div>
<div className="custom-date-input">
<input
type="date"
value={date}
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setDate(e.target.value);
}}
onClick={(e) => e.stopPropagation()}
className="date-input"
/>
</div>
</div>
<div className="picker-section">
<h4>选择时间</h4>
<div className="quick-time-options">
{timeOptions.map((option) => (
<button
key={option.value}
type="button"
className={`quick-option ${time === option.value ? 'selected' : ''}`}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setTime(option.value);
}}
>
{option.label}
</button>
))}
</div>
<div className="custom-time-input">
<Clock size={16} />
<input
type="time"
value={time}
onChange={(e) => {
e.preventDefault();
e.stopPropagation();
setTime(e.target.value);
}}
onClick={(e) => e.stopPropagation()}
className="time-input"
/>
</div>
</div>
<div className="picker-actions">
<button
type="button"
className="action-btn cancel"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setShowQuickSelect(false);
}}
>
取消
</button>
<button
type="button"
className="action-btn confirm"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setShowQuickSelect(false);
}}
>
确认
</button>
</div>
</div>
)}
{showQuickSelect && (
<div
className="datetime-picker-overlay"
onClick={() => setShowQuickSelect(false)}
/>
)}
</div>
);
};
export default DateTimePicker;

View File

@ -34,7 +34,7 @@ const MeetingTimeline = ({ meetingsByDate, currentUser, onDeleteMeeting }) => {
return date.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric' });
};
const truncateSummary = (summary, maxLines = 3, maxLength = 80) => {
const truncateSummary = (summary, maxLines = 3, maxLength = 100) => {
if (!summary) return '暂无摘要';
// Split by lines and check line count
@ -59,7 +59,7 @@ const MeetingTimeline = ({ meetingsByDate, currentUser, onDeleteMeeting }) => {
return summary;
};
const shouldShowMoreButton = (summary, maxLines = 3, maxLength = 80) => {
const shouldShowMoreButton = (summary, maxLines = 3, maxLength = 100) => {
if (!summary) return false;
const lines = summary.split('\n');
return lines.length > maxLines || summary.length > maxLength;

View File

@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { ArrowLeft, Upload, Users, Calendar, FileText, X, User, Plus } from 'lucide-react';
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
import DateTimePicker from '../components/DateTimePicker';
import './CreateMeeting.css';
const CreateMeeting = ({ user }) => {
@ -169,20 +170,14 @@ const CreateMeeting = ({ user }) => {
</div>
<div className="form-group">
<label htmlFor="meeting_time">
<label>
<Calendar size={18} />
会议时间
</label>
<input
type="datetime-local"
id="meeting_time"
name="meeting_time"
<DateTimePicker
value={formData.meeting_time}
onChange={handleInputChange}
onBlur={(e) => {
// Force input to lose focus to save the value
e.target.blur();
}}
onChange={(value) => setFormData(prev => ({ ...prev, meeting_time: value }))}
placeholder="选择会议时间(可选)"
/>
</div>

View File

@ -5,6 +5,7 @@ import { ArrowLeft, Users, Calendar, FileText, X, User, Save, Upload, Plus, Imag
import MDEditor, * as commands from '@uiw/react-md-editor';
import '@uiw/react-md-editor/markdown-editor.css';
import { buildApiUrl, API_ENDPOINTS, API_BASE_URL } from '../config/api';
import DateTimePicker from '../components/DateTimePicker';
import './EditMeeting.css';
const EditMeeting = ({ user }) => {
@ -48,8 +49,7 @@ const EditMeeting = ({ user }) => {
setMeeting(meetingData);
setFormData({
title: meetingData.title,
meeting_time: meetingData.meeting_time ?
new Date(meetingData.meeting_time).toISOString().slice(0, 16) : '',
meeting_time: meetingData.meeting_time || '',
summary: meetingData.summary || '',
attendees: meetingData.attendees || []
});
@ -378,20 +378,14 @@ const EditMeeting = ({ user }) => {
</div>
<div className="form-group">
<label htmlFor="meeting_time">
<label>
<Calendar size={18} />
会议时间
</label>
<input
type="datetime-local"
id="meeting_time"
name="meeting_time"
<DateTimePicker
value={formData.meeting_time}
onChange={handleInputChange}
onBlur={(e) => {
// Force input to lose focus to save the value
e.target.blur();
}}
onChange={(value) => setFormData(prev => ({ ...prev, meeting_time: value }))}
placeholder="选择会议时间(可选)"
/>
</div>

View File

@ -12,6 +12,55 @@
align-items: center;
}
.meeting-actions {
display: flex;
gap: 0.5rem;
}
.action-btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border: none;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
}
.summary-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.summary-btn:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-1px);
}
.edit-btn {
background: #f59e0b;
color: white;
}
.edit-btn:hover {
background: #d97706;
transform: translateY(-1px);
}
.delete-btn {
background: #ef4444;
color: white;
}
.delete-btn:hover {
background: #dc2626;
transform: translateY(-1px);
}
.details-layout {
max-width: 1400px;
margin: 0 auto;
@ -385,13 +434,171 @@
cursor: pointer;
}
/* Meeting Summary Section */
.summary-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.summary-header h2 {
margin: 0;
color: #334155;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.25rem;
}
.export-pdf-btn-main {
display: flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 10px 16px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.export-pdf-btn-main svg {
color: white;
}
.export-pdf-btn-main:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.summary-content {
background: #fafbfc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 0;
min-height: 200px;
overflow: hidden;
}
.markdown-content {
padding: 2rem;
background: white;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
color: #1e293b;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.markdown-content h1 { font-size: 1.5rem; }
.markdown-content h2 { font-size: 1.375rem; }
.markdown-content h3 { font-size: 1.25rem; }
.markdown-content h4 { font-size: 1.125rem; }
.markdown-content p {
line-height: 1.6;
color: #475569;
margin-bottom: 1rem;
}
.markdown-content ul,
.markdown-content ol {
margin: 1rem 0;
padding-left: 1.5rem;
}
.markdown-content li {
margin-bottom: 0.5rem;
line-height: 1.5;
color: #475569;
}
.markdown-content strong {
color: #1e293b;
font-weight: 600;
}
.markdown-content code {
background: #f1f5f9;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Consolas', monospace;
font-size: 0.875rem;
color: #be185d;
}
.no-summary {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
padding: 2rem;
}
.no-summary-content {
text-align: center;
color: #64748b;
}
.no-summary-content svg {
color: #cbd5e1;
margin-bottom: 1rem;
}
.no-summary-content h3 {
margin: 0 0 0.5rem 0;
color: #475569;
font-size: 1.125rem;
}
.no-summary-content p {
margin: 0 0 1.5rem 0;
color: #64748b;
}
.generate-summary-cta {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.generate-summary-cta:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
/* Transcript Sidebar */
.transcript-sidebar {
background: white;
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
height: fit-content;
max-height: calc(100vh - 200px);
height: calc(100vh - 120px);
min-height: 600px;
display: flex;
flex-direction: column;
}
@ -410,13 +617,12 @@
align-items: center;
}
.edit-speakers-btn {
.edit-speakers-btn,
.ai-summary-btn {
display: flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 12px;
@ -424,10 +630,24 @@
transition: background-color 0.2s;
}
.edit-speakers-btn {
background: #007bff;
color: white;
}
.edit-speakers-btn:hover {
background: #0056b3;
}
.ai-summary-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.ai-summary-btn:hover {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
}
.transcript-header h3 {
margin: 0;
color: #334155;
@ -437,22 +657,6 @@
font-size: 1.1rem;
}
.toggle-transcript {
background: #f1f5f9;
border: none;
border-radius: 6px;
padding: 0.5rem 1rem;
color: #475569;
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
transition: all 0.3s ease;
}
.toggle-transcript:hover {
background: #e2e8f0;
}
.transcript-content {
padding: 1rem;
overflow-y: auto;
@ -464,13 +668,21 @@
padding: 1rem;
background: #f8fafc;
border-radius: 8px;
border-left: 3px solid #667eea;
cursor: pointer;
transition: background-color 0.3s ease;
border-left: 3px solid transparent;
transition: all 0.3s ease;
}
.transcript-item.active {
background: #eff6ff;
border-left-color: #667eea;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
transform: translateX(2px);
}
.transcript-item:hover {
background-color: #f0f2ff;
background: #f1f5f9;
cursor: pointer;
transition: background-color 0.3s ease;
}
.transcript-header-item {
@ -495,9 +707,13 @@
}
.speaker-name {
font-weight: 600;
color: #334155;
font-size: 0.9rem;
font-weight: 700;
color: #1e293b;
font-size: 0.95rem;
background: #f1f5f9;
padding: 4px 10px;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.timestamp {
@ -849,6 +1065,216 @@
border-color: #9ca3af;
}
/* AI Summary Modal Styles */
.summary-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.summary-modal {
background: white;
border-radius: 12px;
width: 90%;
max-width: 800px;
max-height: 90vh;
overflow: hidden;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.summary-modal-content {
padding: 0 24px 24px;
max-height: calc(90vh - 120px);
overflow-y: auto;
}
.summary-input-section {
margin-bottom: 2rem;
}
.summary-input-section h4 {
margin: 0 0 1rem 0;
color: #1e293b;
font-size: 1.125rem;
}
.input-description {
color: #64748b;
font-size: 0.875rem;
margin-bottom: 1rem;
}
.user-prompt-input {
width: 100%;
padding: 12px 16px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
margin-bottom: 1rem;
resize: vertical;
transition: all 0.2s ease;
}
.user-prompt-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.generate-summary-btn {
display: flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 12px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.generate-summary-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #5a67d8, #6b46c1);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.generate-summary-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.loading-spinner.small {
width: 16px;
height: 16px;
}
.summary-result-section,
.summary-history-section {
border-top: 1px solid #e2e8f0;
padding-top: 2rem;
margin-top: 2rem;
}
.summary-result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.summary-result-section h4,
.summary-history-section h4 {
margin: 0;
color: #1e293b;
font-size: 1.125rem;
}
.export-pdf-btn {
display: flex;
align-items: center;
gap: 6px;
background: #059669;
color: white;
border: none;
padding: 8px 14px;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.3);
}
.export-pdf-btn svg {
color: white;
}
.export-pdf-btn:hover {
background: #047857;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(5, 150, 105, 0.4);
}
.summary-result-content {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 1.5rem;
max-height: 400px;
overflow-y: auto;
}
.summary-history-list {
display: flex;
flex-direction: column;
gap: 1rem;
max-height: 300px;
overflow-y: auto;
}
.summary-history-item {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 1rem;
transition: all 0.2s ease;
}
.summary-history-item:hover {
background: #f1f5f9;
border-color: #cbd5e1;
}
.summary-history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.summary-date {
color: #64748b;
font-size: 0.875rem;
}
.user-prompt-tag {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 500;
}
.user-prompt-display {
background: white;
border-left: 3px solid #667eea;
padding: 8px 12px;
margin-bottom: 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
}
.summary-content-preview {
color: #475569;
font-size: 0.875rem;
line-height: 1.5;
}
/* Responsive Design */
@media (max-width: 1200px) {
.details-layout {
@ -939,3 +1365,144 @@
justify-content: center;
}
}
/* Transcript Edit Modal */
.transcript-edit-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.transcript-edit-modal {
background: white;
border-radius: 12px;
width: 90%;
max-width: 800px;
max-height: 90vh;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.transcript-edit-content {
padding: 20px;
max-height: calc(90vh - 120px);
overflow-y: auto;
}
.modal-description {
margin-bottom: 20px;
color: #64748b;
font-size: 14px;
}
.transcript-edit-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.transcript-edit-item {
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 16px;
background: #f8fafc;
}
.transcript-edit-item.current {
border-color: #3b82f6;
background: #eff6ff;
}
.transcript-edit-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.current-indicator {
background: #3b82f6;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
}
.transcript-edit-textarea {
width: 100%;
min-height: 80px;
border: 1px solid #d1d5db;
border-radius: 6px;
padding: 12px;
font-family: inherit;
font-size: 14px;
line-height: 1.5;
resize: vertical;
}
.transcript-edit-textarea:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Audio Divider and Subtitle Display */
.audio-divider {
margin: 20px 0;
height: 1px;
background: rgba(255, 255, 255, 0.2);
border-radius: 1px;
}
.subtitle-display {
margin-top: 20px;
min-height: 80px;
padding: 15px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.subtitle-content {
display: flex;
flex-direction: column;
gap: 8px;
}
.subtitle-text {
color: white;
font-size: 1rem;
line-height: 1.5;
text-align: left;
}
.speaker-indicator {
font-weight: 600;
color: #fbbf24;
font-size: 0.9rem;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
align-self: flex-start;
margin-bottom: 4px;
}
.subtitle-placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
}
.placeholder-text {
color: rgba(255, 255, 255, 0.6);
font-size: 0.9rem;
font-style: italic;
text-align: center;
}

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import axios from 'axios';
import { ArrowLeft, Clock, Users, FileText, User, Calendar, Play, Pause, Volume2, MessageCircle, Edit, Trash2, Settings, Save, X } from 'lucide-react';
import { ArrowLeft, Clock, Users, FileText, User, Calendar, Play, Pause, Volume2, MessageCircle, Edit, Trash2, Settings, Save, X, Edit3, Brain, Sparkles, Download } from 'lucide-react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
@ -27,7 +27,19 @@ const MeetingDetails = ({ user }) => {
const [showSpeakerEdit, setShowSpeakerEdit] = useState(false);
const [editingSpeakers, setEditingSpeakers] = useState({});
const [speakerList, setSpeakerList] = useState([]);
const [showTranscriptEdit, setShowTranscriptEdit] = useState(false);
const [editingTranscriptIndex, setEditingTranscriptIndex] = useState(-1);
const [editingTranscripts, setEditingTranscripts] = useState({});
const [currentSubtitle, setCurrentSubtitle] = useState('');
const [currentSpeaker, setCurrentSpeaker] = useState('');
const [showSummaryModal, setShowSummaryModal] = useState(false);
const [summaryLoading, setSummaryLoading] = useState(false);
const [summaryResult, setSummaryResult] = useState(null);
const [userPrompt, setUserPrompt] = useState('');
const [summaryHistory, setSummaryHistory] = useState([]);
const [currentHighlightIndex, setCurrentHighlightIndex] = useState(-1);
const audioRef = useRef(null);
const transcriptRefs = useRef([]);
useEffect(() => {
fetchMeetingDetails();
@ -133,7 +145,40 @@ const MeetingDetails = ({ user }) => {
const handleTimeUpdate = () => {
if (audioRef.current) {
setCurrentTime(audioRef.current.currentTime);
const currentTime = audioRef.current.currentTime;
setCurrentTime(currentTime);
//
updateSubtitle(currentTime);
}
};
const updateSubtitle = (currentTime) => {
const currentTimeMs = currentTime * 1000;
const currentSegment = transcript.find(item =>
currentTimeMs >= item.start_time_ms && currentTimeMs <= item.end_time_ms
);
if (currentSegment) {
setCurrentSubtitle(currentSegment.text_content);
// 使 speaker_tag
setCurrentSpeaker(currentSegment.speaker_tag || `发言人 ${currentSegment.speaker_id}`);
// segmenttranscript
const currentIndex = transcript.findIndex(item => item.segment_id === currentSegment.segment_id);
setCurrentHighlightIndex(currentIndex);
//
if (currentIndex !== -1 && transcriptRefs.current[currentIndex]) {
transcriptRefs.current[currentIndex].scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
} else {
setCurrentSubtitle('');
setCurrentSpeaker('');
setCurrentHighlightIndex(-1);
}
};
@ -271,6 +316,247 @@ const MeetingDetails = ({ user }) => {
setShowSpeakerEdit(true);
};
const handleTranscriptEdit = (index) => {
setEditingTranscriptIndex(index);
//
const editItems = [];
if (index > 0) editItems.push({ ...transcript[index - 1], originalIndex: index - 1 });
editItems.push({ ...transcript[index], originalIndex: index });
if (index < transcript.length - 1) editItems.push({ ...transcript[index + 1], originalIndex: index + 1 });
//
const initialEditState = {};
editItems.forEach(item => {
initialEditState[item.originalIndex] = item.text_content;
});
setEditingTranscripts(initialEditState);
setShowTranscriptEdit(true);
};
const handleTranscriptTextChange = (index, newText) => {
setEditingTranscripts(prev => ({
...prev,
[index]: newText
}));
};
const handleSaveTranscriptEdits = async () => {
try {
const baseUrl = "";
const updates = Object.entries(editingTranscripts).map(([index, text_content]) => ({
segment_id: transcript[index].segment_id,
text_content: text_content
}));
await axios.put(`${baseUrl}/api/meetings/${meeting_id}/transcript/batch`, {
updates: updates
});
//
setTranscript(prev => prev.map((item, idx) => {
const newText = editingTranscripts[idx];
return newText !== undefined ? { ...item, text_content: newText } : item;
}));
setShowTranscriptEdit(false);
setEditingTranscripts({});
setEditingTranscriptIndex(-1);
} catch (err) {
console.error('Error updating transcript:', err);
setError('更新转录内容失败,请重试');
}
};
const getEditingItems = () => {
if (editingTranscriptIndex === -1) return [];
const items = [];
const index = editingTranscriptIndex;
if (index > 0) items.push({ ...transcript[index - 1], originalIndex: index - 1, position: 'prev' });
items.push({ ...transcript[index], originalIndex: index, position: 'current' });
if (index < transcript.length - 1) items.push({ ...transcript[index + 1], originalIndex: index + 1, position: 'next' });
return items;
};
// AI
const generateSummary = async () => {
if (summaryLoading) return;
setSummaryLoading(true);
try {
const baseUrl = "";
const response = await axios.post(`${baseUrl}/api/meetings/${meeting_id}/generate-summary`, {
user_prompt: userPrompt
});
setSummaryResult(response.data);
//
await fetchSummaryHistory();
} catch (err) {
console.error('Error generating summary:', err);
setError('生成AI总结失败请重试');
} finally {
setSummaryLoading(false);
}
};
const fetchSummaryHistory = async () => {
try {
const baseUrl = "";
const response = await axios.get(`${baseUrl}/api/meetings/${meeting_id}/summaries`);
setSummaryHistory(response.data.summaries);
} catch (err) {
console.error('Error fetching summary history:', err);
}
};
const openSummaryModal = async () => {
setShowSummaryModal(true);
setUserPrompt('');
setSummaryResult(null);
await fetchSummaryHistory();
};
const exportToPDF = async () => {
try {
//
let summaryContent = summaryResult?.content ||
meeting?.summary ||
(summaryHistory.length > 0 ? summaryHistory[0].content : null);
if (!summaryContent) {
alert('暂无会议总结内容请先生成AI总结。');
return;
}
// ReactMarkdown
const tempDiv = document.createElement('div');
tempDiv.style.position = 'fixed';
tempDiv.style.top = '-9999px';
tempDiv.style.width = '800px';
tempDiv.style.padding = '20px';
tempDiv.style.backgroundColor = 'white';
// markdown-to-html
const ReactMarkdown = (await import('react-markdown')).default;
const { createRoot } = await import('react-dom/client');
document.body.appendChild(tempDiv);
const root = createRoot(tempDiv);
// MarkdownHTML
await new Promise((resolve) => {
root.render(
React.createElement(ReactMarkdown, {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeRaw, rehypeSanitize],
children: summaryContent
})
);
setTimeout(resolve, 100); //
});
const renderedHTML = tempDiv.innerHTML;
// HTMLPDF
const printContainer = document.createElement('div');
printContainer.style.position = 'fixed';
printContainer.style.top = '-9999px';
printContainer.style.width = '210mm';
printContainer.style.padding = '20mm';
printContainer.style.backgroundColor = 'white';
printContainer.style.fontFamily = 'Arial, sans-serif';
printContainer.style.fontSize = '14px';
printContainer.style.lineHeight = '1.6';
printContainer.style.color = '#333';
// PDFHTML使Markdown
const meetingTime = formatDateTime(meeting.meeting_time);
const attendeesList = meeting.attendees.map(attendee =>
typeof attendee === 'string' ? attendee : attendee.caption
).join('、');
printContainer.innerHTML = `
<div>
<h1 style="color: #2563eb; margin-bottom: 30px; font-size: 24px; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px;">
${meeting.title || '会议总结'}
</h1>
<div style="margin-bottom: 30px; background: #f9fafb; padding: 20px; border-radius: 8px;">
<h2 style="color: #374151; font-size: 16px; margin-bottom: 15px;">会议信息</h2>
<p style="margin: 8px 0;"><strong>会议时间</strong>${meetingTime}</p>
<p style="margin: 8px 0;"><strong>创建人</strong>${meeting.creator_username}</p>
<p style="margin: 8px 0;"><strong>参会人数</strong>${meeting.attendees.length}</p>
<p style="margin: 8px 0;"><strong>参会人员</strong>${attendeesList}</p>
</div>
<div style="margin-bottom: 30px;">
<h2 style="color: #374151; font-size: 16px; margin-bottom: 15px;">会议摘要</h2>
<div style="line-height: 1.8;">${renderedHTML}</div>
</div>
<div style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #e5e7eb; font-size: 12px; color: #6b7280;">
<p>导出时间${new Date().toLocaleString('zh-CN')}</p>
</div>
</div>
`;
document.body.appendChild(printContainer);
// 使PDF
const originalContent = document.body.innerHTML;
const originalTitle = document.title;
//
document.body.innerHTML = printContainer.innerHTML;
document.title = `${meeting.title || '会议总结'}_${new Date().toISOString().split('T')[0]}`;
//
const printStyles = document.createElement('style');
printStyles.innerHTML = `
@media print {
body { margin: 0; padding: 20px; font-family: 'Microsoft YaHei', Arial, sans-serif; }
h1 { page-break-before: avoid; }
h2 { page-break-before: avoid; }
h3 { margin-top: 1.5rem; margin-bottom: 0.75rem; color: #1e293b; }
h4 { margin-top: 1rem; margin-bottom: 0.5rem; color: #1e293b; }
p { margin-bottom: 0.75rem; color: #475569; line-height: 1.6; }
ul, ol { margin: 0.75rem 0; padding-left: 1.5rem; }
li { margin-bottom: 0.25rem; color: #475569; }
strong { color: #1e293b; font-weight: 600; }
code { background: #f1f5f9; padding: 2px 4px; border-radius: 3px; color: #dc2626; }
.page-break { page-break-before: always; }
}
`;
document.head.appendChild(printStyles);
//
window.print();
//
setTimeout(() => {
document.body.innerHTML = originalContent;
document.title = originalTitle;
document.head.removeChild(printStyles);
document.body.removeChild(printContainer);
document.body.removeChild(tempDiv);
// React
window.location.reload();
}, 1000);
} catch (error) {
console.error('PDF导出失败:', error);
alert('PDF导出失败请重试。建议使用浏览器的打印功能并选择"保存为PDF"。');
}
};
const isCreator = meeting && user && String(meeting.creator_id) === String(user.user_id);
if (loading) {
@ -409,6 +695,27 @@ const MeetingDetails = ({ user }) => {
/>
</div>
</div>
{/* 分割线 */}
<div className="audio-divider"></div>
{/* 动态字幕显示 */}
<div className="subtitle-display">
{currentSubtitle ? (
<div className="subtitle-content">
<div className="speaker-indicator">
{currentSpeaker}
</div>
<div className="subtitle-text">
{currentSubtitle}
</div>
</div>
) : (
<div className="subtitle-placeholder">
<div className="placeholder-text">播放音频时将在此处显示实时字幕</div>
</div>
)}
</div>
</div>
) : (
<div className="no-audio">
@ -422,16 +729,47 @@ const MeetingDetails = ({ user }) => {
</section>
<section className="card-section">
<h2><FileText size={20} /> 会议摘要</h2>
<div className="summary-content">
<div className="markdown-content">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
<div className="summary-header">
<h2><FileText size={20} /> 会议摘要</h2>
{meeting?.summary && (
<button
className="export-pdf-btn-main"
onClick={exportToPDF}
title="导出PDF"
>
{meeting.summary || '暂无摘要信息。'}
</ReactMarkdown>
</div>
<Download size={16} />
<span>导出PDF</span>
</button>
)}
</div>
<div className="summary-content">
{meeting?.summary ? (
<div className="markdown-content">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
>
{meeting.summary}
</ReactMarkdown>
</div>
) : (
<div className="no-summary">
<div className="no-summary-content">
<FileText size={48} />
<h3>暂无会议总结</h3>
<p>该会议尚未生成总结内容</p>
{isCreator && (
<button
className="generate-summary-cta"
onClick={openSummaryModal}
>
<Brain size={16} />
<span>生成AI总结</span>
</button>
)}
</div>
</div>
)}
</div>
</section>
</div>
@ -443,30 +781,34 @@ const MeetingDetails = ({ user }) => {
<h3><MessageCircle size={20} /> 对话转录</h3>
<div className="transcript-controls">
{isCreator && (
<button
className="edit-speakers-btn"
onClick={handleSpeakerEditOpen}
title="编辑发言人标签"
>
<Settings size={16} />
<span>编辑标签</span>
</button>
<>
<button
className="edit-speakers-btn"
onClick={handleSpeakerEditOpen}
title="编辑发言人标签"
>
<Settings size={16} />
<span>编辑标签</span>
</button>
<button
className="ai-summary-btn"
onClick={openSummaryModal}
title="AI总结"
>
<Brain size={16} />
<span>AI总结</span>
</button>
</>
)}
<button
className="toggle-transcript"
onClick={() => setShowTranscript(!showTranscript)}
>
{showTranscript ? '隐藏' : '显示'}
</button>
</div>
</div>
{showTranscript && (
<div className="transcript-content">
{transcript.map((item) => (
<div className="transcript-content">
{transcript.map((item, index) => (
<div
key={item.segment_id}
className="transcript-item"
ref={(el) => transcriptRefs.current[index] = el}
className={`transcript-item ${currentHighlightIndex === index ? 'active' : ''}`}
>
<div className="transcript-header-item">
<span
@ -484,6 +826,15 @@ const MeetingDetails = ({ user }) => {
>
{formatTime(item.start_time_ms / 1000)}
</span>
{isCreator && (
<button
className="edit-transcript-btn"
onClick={() => handleTranscriptEdit(index)}
title="编辑转录内容"
>
<Edit3 size={14} />
</button>
)}
</div>
</div>
<div
@ -496,7 +847,6 @@ const MeetingDetails = ({ user }) => {
</div>
))}
</div>
)}
</div>
</div>
@ -587,6 +937,166 @@ const MeetingDetails = ({ user }) => {
</div>
</div>
)}
{/* Transcript Edit Modal */}
{showTranscriptEdit && (
<div className="transcript-edit-modal-overlay" onClick={() => setShowTranscriptEdit(false)}>
<div className="transcript-edit-modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h3>编辑转录内容</h3>
<button
className="close-btn"
onClick={() => setShowTranscriptEdit(false)}
>
<X size={20} />
</button>
</div>
<div className="transcript-edit-content">
<p className="modal-description">修改选中转录条目及其上下文内容</p>
<div className="transcript-edit-list">
{getEditingItems().map((item) => (
<div key={item.originalIndex} className={`transcript-edit-item ${item.position}`}>
<div className="transcript-edit-header">
<span className="speaker-name">{item.speaker_tag}</span>
<span className="timestamp">{formatTime(item.start_time_ms / 1000)}</span>
{item.position === 'current' && (
<span className="current-indicator">当前编辑</span>
)}
</div>
<textarea
value={editingTranscripts[item.originalIndex] || item.text_content}
onChange={(e) => handleTranscriptTextChange(item.originalIndex, e.target.value)}
className="transcript-edit-textarea"
rows={3}
placeholder="输入转录内容..."
/>
</div>
))}
</div>
</div>
<div className="modal-actions">
<button
className="btn-cancel"
onClick={() => setShowTranscriptEdit(false)}
>
取消
</button>
<button
className="btn-save"
onClick={handleSaveTranscriptEdits}
>
<Save size={16} />
保存修改
</button>
</div>
</div>
</div>
)}
{/* AI Summary Modal */}
{showSummaryModal && (
<div className="summary-modal-overlay" onClick={() => setShowSummaryModal(false)}>
<div className="summary-modal" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h3><Brain size={20} /> AI会议总结</h3>
<button
className="close-btn"
onClick={() => setShowSummaryModal(false)}
>
<X size={20} />
</button>
</div>
<div className="summary-modal-content">
<div className="summary-input-section">
<h4>生成新的总结</h4>
<p className="input-description">
系统将使用通用提示词分析会议转录您可以添加额外要求
</p>
<textarea
value={userPrompt}
onChange={(e) => setUserPrompt(e.target.value)}
className="user-prompt-input"
placeholder="请输入您希望AI重点关注的内容请重点分析决策事项和待办任务..."
rows={3}
/>
<button
className="generate-summary-btn"
onClick={generateSummary}
disabled={summaryLoading}
>
{summaryLoading ? (
<>
<div className="loading-spinner small"></div>
<span>正在生成总结...</span>
</>
) : (
<>
<Sparkles size={16} />
<span>生成AI总结</span>
</>
)}
</button>
</div>
{summaryResult && (
<div className="summary-result-section">
<div className="summary-result-header">
<h4>最新生成的总结</h4>
<button
className="export-pdf-btn"
onClick={exportToPDF}
title="导出PDF"
>
<Download size={14} />
<span>导出PDF</span>
</button>
</div>
<div className="summary-result-content">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
>
{summaryResult.content}
</ReactMarkdown>
</div>
</div>
)}
{summaryHistory.length > 0 && (
<div className="summary-history-section">
<h4>历史总结记录</h4>
<div className="summary-history-list">
{summaryHistory.map((summary, index) => (
<div key={summary.id} className="summary-history-item">
<div className="summary-history-header">
<span className="summary-date">
{new Date(summary.created_at).toLocaleString('zh-CN')}
</span>
{summary.user_prompt && (
<span className="user-prompt-tag">自定义要求</span>
)}
</div>
{summary.user_prompt && (
<div className="user-prompt-display">
<strong>用户要求</strong>{summary.user_prompt}
</div>
)}
<div className="summary-content-preview">
{summary.content.substring(0, 200)}...
</div>
</div>
))}
</div>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
};

View File

@ -7,7 +7,7 @@ export default defineConfig({
server: {
host: true, // Optional: Allows the server to be accessible externally
port: 5173, // Optional: Specify a port if needed
allowedHosts: ['c0e02ee.r9.cpolar.top'], // Add the problematic hostname here
allowedHosts: ['6fc3f0b0.r3.cpolar.cn'], // Add the problematic hostname here
proxy: {
'/api': {
target: 'http://localhost:8000', // 后端服务地址

397
yarn.lock
View File

@ -138,6 +138,11 @@
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/runtime@^7.12.5", "@babel/runtime@^7.26.9":
version "7.28.3"
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.3.tgz#75c5034b55ba868121668be5d5bb31cc64e6e61a"
integrity sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==
"@babel/runtime@^7.14.6", "@babel/runtime@^7.17.2":
version "7.28.2"
resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.2.tgz"
@ -173,11 +178,136 @@
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@esbuild/aix-ppc64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz#a1414903bb38027382f85f03dda6065056757727"
integrity sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==
"@esbuild/android-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz#c859994089e9767224269884061f89dae6fb51c6"
integrity sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==
"@esbuild/android-arm@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz#96a8f2ca91c6cd29ea90b1af79d83761c8ba0059"
integrity sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==
"@esbuild/android-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz#a3a626c4fec4a024a9fa8c7679c39996e92916f0"
integrity sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==
"@esbuild/darwin-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz"
integrity sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==
"@esbuild/darwin-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz#5271b0df2bb12ce8df886704bfdd1c7cc01385d2"
integrity sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==
"@esbuild/freebsd-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz#d0a0e7fdf19733b8bb1566b81df1aa0bb7e46ada"
integrity sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==
"@esbuild/freebsd-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz#2de8b2e0899d08f1cb1ef3128e159616e7e85343"
integrity sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==
"@esbuild/linux-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz#a4209efadc0c2975716458484a4e90c237c48ae9"
integrity sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==
"@esbuild/linux-arm@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz#ccd9e291c24cd8d9142d819d463e2e7200d25b19"
integrity sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==
"@esbuild/linux-ia32@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz#006ad1536d0c2b28fb3a1cf0b53bcb85aaf92c4d"
integrity sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==
"@esbuild/linux-loong64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz#127b3fbfb2c2e08b1397e985932f718f09a8f5c4"
integrity sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==
"@esbuild/linux-mips64el@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz#837d1449517791e3fa7d82675a2d06d9f56cb340"
integrity sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==
"@esbuild/linux-ppc64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz#aa2e3bd93ab8df084212f1895ca4b03c42d9e0fe"
integrity sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==
"@esbuild/linux-riscv64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz#a340620e31093fef72767dd28ab04214b3442083"
integrity sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==
"@esbuild/linux-s390x@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz#ddfed266c8c13f5efb3105a0cd47f6dcd0e79e71"
integrity sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==
"@esbuild/linux-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz#9a4f78c75c051e8c060183ebb39a269ba936a2ac"
integrity sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==
"@esbuild/netbsd-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz#902c80e1d678047926387230bc037e63e00697d0"
integrity sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==
"@esbuild/netbsd-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz#2d9eb4692add2681ff05a14ce99de54fbed7079c"
integrity sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==
"@esbuild/openbsd-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz#89c3b998c6de739db38ab7fb71a8a76b3fa84a45"
integrity sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==
"@esbuild/openbsd-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz#2f01615cf472b0e48c077045cfd96b5c149365cc"
integrity sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==
"@esbuild/openharmony-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz#a201f720cd2c3ebf9a6033fcc3feb069a54b509a"
integrity sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==
"@esbuild/sunos-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz#07046c977985a3334667f19e6ab3a01a80862afb"
integrity sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==
"@esbuild/win32-arm64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz#4a5470caf0d16127c05d4833d4934213c69392d1"
integrity sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==
"@esbuild/win32-ia32@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz#3de3e8470b7b328d99dbc3e9ec1eace207e5bbc4"
integrity sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==
"@esbuild/win32-x64@0.25.8":
version "0.25.8"
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz#610d7ea539d2fcdbe39237b5cc175eb2c4451f9c"
integrity sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==
"@eslint-community/eslint-utils@^4.2.0":
version "4.7.0"
resolved "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz"
@ -226,7 +356,7 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
"@eslint/js@^9.30.1", "@eslint/js@9.32.0":
"@eslint/js@9.32.0", "@eslint/js@^9.30.1":
version "9.32.0"
resolved "https://registry.npmmirror.com/@eslint/js/-/js-9.32.0.tgz"
integrity sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==
@ -303,11 +433,106 @@
resolved "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz"
integrity sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==
"@rollup/rollup-android-arm-eabi@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.1.tgz#c659481d5b15054d4636b3dd0c2f50ab3083d839"
integrity sha512-oENme6QxtLCqjChRUUo3S6X8hjCXnWmJWnedD7VbGML5GUtaOtAyx+fEEXnBXVf0CBZApMQU0Idwi0FmyxzQhw==
"@rollup/rollup-android-arm64@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.1.tgz#7e05c3c0bf6a79ee6b40ab5e778679742f06815d"
integrity sha512-OikvNT3qYTl9+4qQ9Bpn6+XHM+ogtFadRLuT2EXiFQMiNkXFLQfNVppi5o28wvYdHL2s3fM0D/MZJ8UkNFZWsw==
"@rollup/rollup-darwin-arm64@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.1.tgz"
integrity sha512-EFYNNGij2WllnzljQDQnlFTXzSJw87cpAs4TVBAWLdkvic5Uh5tISrIL6NRcxoh/b2EFBG/TK8hgRrGx94zD4A==
"@rollup/rollup-darwin-x64@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.1.tgz#a4df7fa06ac318b66a6aa66d6f1e0a58fef58cd3"
integrity sha512-ZaNH06O1KeTug9WI2+GRBE5Ujt9kZw4a1+OIwnBHal92I8PxSsl5KpsrPvthRynkhMck4XPdvY0z26Cym/b7oA==
"@rollup/rollup-freebsd-arm64@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.1.tgz#6634478a78a0c17dcf55adb621fa66faa58a017b"
integrity sha512-n4SLVebZP8uUlJ2r04+g2U/xFeiQlw09Me5UFqny8HGbARl503LNH5CqFTb5U5jNxTouhRjai6qPT0CR5c/Iig==
"@rollup/rollup-freebsd-x64@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.1.tgz#db42c46c0263b2562e2ba5c2e00e318646f2b24c"
integrity sha512-8vu9c02F16heTqpvo3yeiu7Vi1REDEC/yES/dIfq3tSXe6mLndiwvYr3AAvd1tMNUqE9yeGYa5w7PRbI5QUV+w==
"@rollup/rollup-linux-arm-gnueabihf@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.1.tgz#88ca443ad42c70555978b000c6d1dd925fb3203b"
integrity sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==
"@rollup/rollup-linux-arm-musleabihf@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.1.tgz#36106fe103d32c2a97583ebadcfb28dc63988bda"
integrity sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==
"@rollup/rollup-linux-arm64-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.1.tgz#00c28bc9210dcfbb5e7fa8e52fd827fb570afe26"
integrity sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==
"@rollup/rollup-linux-arm64-musl@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.1.tgz#45a13486b5523235eb87b349e7ca5a0bb85a5b0e"
integrity sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==
"@rollup/rollup-linux-loongarch64-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.1.tgz#b8edd99f072cd652acbbddc1c539b1ac4254381d"
integrity sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==
"@rollup/rollup-linux-ppc64-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.1.tgz#0ec72a4f8b7a86b13c0f6b7666ed1d3b6e8e67cc"
integrity sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==
"@rollup/rollup-linux-riscv64-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.1.tgz#99f06928528fb58addd12e50827e1a0269c1cca8"
integrity sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==
"@rollup/rollup-linux-riscv64-musl@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.1.tgz#3c14aba63b4170fe3d9d0b6ad98361366170590e"
integrity sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==
"@rollup/rollup-linux-s390x-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.1.tgz#34c647a823dcdca0f749a2bdcbde4fb131f37a4c"
integrity sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==
"@rollup/rollup-linux-x64-gnu@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.1.tgz#3991010418c005e8791c415e7c2072b247157710"
integrity sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==
"@rollup/rollup-linux-x64-musl@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.1.tgz#f3943e5f284f40ffbcf4a14da9ee2e43d303b462"
integrity sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==
"@rollup/rollup-win32-arm64-msvc@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.1.tgz#45b5a1d3f0af63f85044913c371d7b0519c913ad"
integrity sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==
"@rollup/rollup-win32-ia32-msvc@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.1.tgz#900ef7211d2929e9809f3a044c4e2fd3aa685a0c"
integrity sha512-7Thzy9TMXDw9AU4f4vsLNBxh7/VOKuXi73VH3d/kHGr0tZ3x/ewgL9uC7ojUKmH1/zvmZe2tLapYcZllk3SO8Q==
"@rollup/rollup-win32-x64-msvc@4.46.1":
version "4.46.1"
resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.1.tgz#932d8696dfef673bee1a1e291a5531d25a6903be"
integrity sha512-7GVB4luhFmGUNXXJhH2jJwZCFB3pIOixv2E3s17GQHBFUOQaISlt7aGcQgqvCaDSxTZJUzlK/QJ1FN8S94MrzQ==
"@types/babel__core@^7.20.5":
version "7.20.5"
resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz"
@ -355,7 +580,7 @@
dependencies:
"@types/estree" "*"
"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.6", "@types/estree@1.0.8":
"@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
version "1.0.8"
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz"
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
@ -391,11 +616,21 @@
resolved "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz"
integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==
"@types/pako@^2.0.3":
version "2.0.4"
resolved "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz#c3575ef8125e176c345fa0e7b301c1db41170c15"
integrity sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==
"@types/prismjs@^1.0.0":
version "1.26.5"
resolved "https://registry.npmmirror.com/@types/prismjs/-/prismjs-1.26.5.tgz"
integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==
"@types/raf@^3.4.0":
version "3.4.3"
resolved "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz#85f1d1d17569b28b8db45e16e996407a56b0ab04"
integrity sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==
"@types/react-dom@^19.1.6":
version "19.1.6"
resolved "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.1.6.tgz"
@ -408,17 +643,17 @@
dependencies:
csstype "^3.0.2"
"@types/trusted-types@^2.0.7":
version "2.0.7"
resolved "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
"@types/unist@*", "@types/unist@^3.0.0":
version "3.0.3"
resolved "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz"
integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==
"@types/unist@^2":
version "2.0.11"
resolved "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz"
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
"@types/unist@^2.0.0":
"@types/unist@^2", "@types/unist@^2.0.0":
version "2.0.11"
resolved "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz"
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
@ -530,6 +765,11 @@ balanced-match@^1.0.0:
resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
bcp-47-match@^2.0.0:
version "2.0.3"
resolved "https://registry.npmmirror.com/bcp-47-match/-/bcp-47-match-2.0.3.tgz"
@ -576,6 +816,20 @@ caniuse-lite@^1.0.30001726:
resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz"
integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==
canvg@^3.0.11:
version "3.0.11"
resolved "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz#4b4290a6c7fa36871fac2b14e432eff33b33cf2b"
integrity sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==
dependencies:
"@babel/runtime" "^7.12.5"
"@types/raf" "^3.4.0"
core-js "^3.8.3"
raf "^3.4.1"
regenerator-runtime "^0.13.7"
rgbcolor "^1.0.1"
stackblur-canvas "^2.0.0"
svg-pathdata "^6.0.3"
ccount@^2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz"
@ -648,6 +902,11 @@ cookie@^1.0.1:
resolved "https://registry.npmmirror.com/cookie/-/cookie-1.0.2.tgz"
integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==
core-js@^3.6.0, core-js@^3.8.3:
version "3.45.1"
resolved "https://registry.npmmirror.com/core-js/-/core-js-3.45.1.tgz#5810e04a1b4e9bc5ddaa4dd12e702ff67300634d"
integrity sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==
cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz"
@ -657,6 +916,13 @@ cross-spawn@^7.0.6:
shebang-command "^2.0.0"
which "^2.0.1"
css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"
css-selector-parser@^3.0.0:
version "3.1.3"
resolved "https://registry.npmmirror.com/css-selector-parser/-/css-selector-parser-3.1.3.tgz"
@ -708,6 +974,13 @@ direction@^2.0.0:
resolved "https://registry.npmmirror.com/direction/-/direction-2.0.1.tgz"
integrity sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==
dompurify@^3.2.4:
version "3.2.6"
resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.6.tgz#ca040a6ad2b88e2a92dc45f38c79f84a714a1cad"
integrity sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==
optionalDependencies:
"@types/trusted-types" "^2.0.7"
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz"
@ -928,11 +1201,25 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-png@^6.2.0:
version "6.4.0"
resolved "https://registry.npmmirror.com/fast-png/-/fast-png-6.4.0.tgz#807fc353ccab060d09151b7d082786e02d8e92d6"
integrity sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==
dependencies:
"@types/pako" "^2.0.3"
iobuffer "^5.3.2"
pako "^2.1.0"
fdir@^6.4.4, fdir@^6.4.6:
version "6.4.6"
resolved "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz"
integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==
fflate@^0.8.1:
version "0.8.2"
resolved "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
file-entry-cache@^8.0.0:
version "8.0.0"
resolved "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz"
@ -1274,6 +1561,14 @@ html-void-elements@^3.0.0:
resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz"
integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==
html2canvas@^1.0.0-rc.5:
version "1.4.1"
resolved "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"
ignore@^5.2.0:
version "5.3.2"
resolved "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz"
@ -1297,6 +1592,11 @@ inline-style-parser@0.2.4:
resolved "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz"
integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==
iobuffer@^5.3.2:
version "5.4.0"
resolved "https://registry.npmmirror.com/iobuffer/-/iobuffer-5.4.0.tgz#f85dff957fd0579257472f0a4cfe5ed3430e63e1"
integrity sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==
is-alphabetical@^2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz"
@ -1379,6 +1679,20 @@ json5@^2.2.3:
resolved "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
jspdf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmmirror.com/jspdf/-/jspdf-3.0.2.tgz#f1e0e7f0954327bea4b8b02008613ad51e6024f6"
integrity sha512-G0fQDJ5fAm6UW78HG6lNXyq09l0PrA1rpNY5i+ly17Zb1fMMFSmS+3lw4cnrAPGyouv2Y0ylujbY2Ieq3DSlKA==
dependencies:
"@babel/runtime" "^7.26.9"
fast-png "^6.2.0"
fflate "^0.8.1"
optionalDependencies:
canvg "^3.0.11"
core-js "^3.6.0"
dompurify "^3.2.4"
html2canvas "^1.0.0-rc.5"
keyv@^4.5.4:
version "4.5.4"
resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz"
@ -1958,6 +2272,11 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
pako@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz"
@ -2000,6 +2319,11 @@ path-key@^3.1.0:
resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz"
@ -2044,6 +2368,13 @@ punycode@^2.1.0:
resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
raf@^3.4.1:
version "3.4.1"
resolved "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
dependencies:
performance-now "^2.1.0"
react-dom@^19.1.0:
version "19.1.1"
resolved "https://registry.npmmirror.com/react-dom/-/react-dom-19.1.1.tgz"
@ -2119,6 +2450,11 @@ refractor@^4.8.0:
hastscript "^7.0.0"
parse-entities "^4.0.0"
regenerator-runtime@^0.13.7:
version "0.13.11"
resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
rehype-attr@~3.0.1:
version "3.0.3"
resolved "https://registry.npmmirror.com/rehype-attr/-/rehype-attr-3.0.3.tgz"
@ -2157,10 +2493,10 @@ rehype-parse@^9.0.0:
hast-util-from-html "^2.0.0"
unified "^11.0.0"
rehype-prism-plus@~2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/rehype-prism-plus/-/rehype-prism-plus-2.0.1.tgz"
integrity sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==
rehype-prism-plus@2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz"
integrity sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==
dependencies:
hast-util-to-string "^3.0.0"
parse-numeric-range "^1.3.0"
@ -2169,10 +2505,10 @@ rehype-prism-plus@~2.0.0:
unist-util-filter "^5.0.0"
unist-util-visit "^5.0.0"
rehype-prism-plus@2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz"
integrity sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==
rehype-prism-plus@~2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/rehype-prism-plus/-/rehype-prism-plus-2.0.1.tgz"
integrity sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==
dependencies:
hast-util-to-string "^3.0.0"
parse-numeric-range "^1.3.0"
@ -2291,6 +2627,11 @@ resolve-from@^4.0.0:
resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
rgbcolor@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d"
integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==
rollup@^4.40.0:
version "4.46.1"
resolved "https://registry.npmmirror.com/rollup/-/rollup-4.46.1.tgz"
@ -2357,6 +2698,11 @@ space-separated-tokens@^2.0.0:
resolved "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz"
integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==
stackblur-canvas@^2.0.0:
version "2.7.0"
resolved "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz#af931277d0b5096df55e1f91c530043e066989b6"
integrity sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==
stringify-entities@^4.0.0:
version "4.0.4"
resolved "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz"
@ -2391,6 +2737,18 @@ supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
svg-pathdata@^6.0.3:
version "6.0.3"
resolved "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac"
integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==
text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"
tinyglobby@^0.2.14:
version "0.2.14"
resolved "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz"
@ -2491,6 +2849,13 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"
vfile-location@^5.0.0:
version "5.0.3"
resolved "https://registry.npmmirror.com/vfile-location/-/vfile-location-5.0.3.tgz"