feat: integrate pixel teacher presence and resilient classroom preview

This commit is contained in:
鲨鱼辣椒
2026-09-23 18:23:53 +08:00
parent 12d800ec51
commit 7951cca700
60 changed files with 3313 additions and 180 deletions

View File

@@ -0,0 +1,9 @@
export const CLASSROOM_SIDEBAR_DEFAULT_WIDTH = 171;
export const CLASSROOM_SIDEBAR_MIN_WIDTH = 128;
export const CLASSROOM_SIDEBAR_MAX_WIDTH = 256;
export function normalizeClassroomSidebarWidth(width: unknown): number {
return typeof width === 'number' && Number.isFinite(width)
? Math.max(CLASSROOM_SIDEBAR_MIN_WIDTH, Math.min(CLASSROOM_SIDEBAR_MAX_WIDTH, Math.round(width)))
: CLASSROOM_SIDEBAR_DEFAULT_WIDTH;
}

View File

@@ -0,0 +1,23 @@
import type { AuthUser } from '@/stores/auth';
export interface CodingLoginRecovery {
id: string;
account: string;
projectId: string;
conversationId: string | null;
work: boolean;
}
export function codingRecoveryAccount(user: AuthUser | null): string {
return user ? JSON.stringify([user.tenantId, user.userId, user.username]) : '';
}
export function readCodingLoginRecovery(value: unknown): CodingLoginRecovery | undefined {
if (!value || typeof value !== 'object') return;
const item = value as Partial<CodingLoginRecovery>;
if (typeof item.id !== 'string' || typeof item.account !== 'string' || !item.account
|| typeof item.projectId !== 'string' || !item.projectId
|| !(item.conversationId === null || typeof item.conversationId === 'string')
|| typeof item.work !== 'boolean') return;
return item as CodingLoginRecovery;
}

View File

@@ -2,6 +2,8 @@ import { hostApiFetch, createHostEventSource, ensureHostApiToken } from './host-
import type {
ConsultationRole,
TeacherAvailability,
TeacherCheckInInput,
TeacherCheckInResult,
TeacherDefinition,
TeacherSend,
TeacherTopic,
@@ -14,6 +16,11 @@ export function teacherTopicsPath(projectId: string, sourceId: string, role?: Co
: `/api/coding/projects/${encodeURIComponent(projectId)}/conversations/${encodeURIComponent(sourceId)}/teacher-topics`;
}
export const teacherApi = {
checkIn: (projectId: string, input: TeacherCheckInInput) =>
hostApiFetch<TeacherCheckInResult>(`/api/coding/projects/${encodeURIComponent(projectId)}/teacher-check-in`, {
method: 'POST',
body: JSON.stringify(input),
}),
config: (role: ConsultationRole = 'teacher') =>
hostApiFetch<TeacherAvailability & { definition: TeacherDefinition | null }>(
`/api/coding/${role}/config`
@@ -28,8 +35,8 @@ export const teacherApi = {
method: 'POST',
body: JSON.stringify({ draftRevision, sampleContext }),
}),
read: (base: string, id: string) =>
hostApiFetch<TeacherTopic>(base + '/' + encodeURIComponent(id)),
read: (base: string, id: string, select = true) =>
hostApiFetch<TeacherTopic>(base + '/' + encodeURIComponent(id) + (select ? '' : '?select=false')),
send: (base: string, id: string, input: TeacherSend) =>
hostApiFetch<TeacherTopic>(base + '/' + encodeURIComponent(id) + '/messages', {
method: 'POST',

View File

@@ -0,0 +1,9 @@
import { hostApiFetch } from './host-api';
import type { WorkPreviewResult } from '../../shared/coding-work-preview';
export function ensureWorkPreview(projectId: string, requestId: string, conversationId?: string | null): Promise<WorkPreviewResult> {
return hostApiFetch('/api/agent-browser/ensure-work', {
method: 'POST',
body: JSON.stringify({ project_id: projectId, request_id: requestId, conversation_id: conversationId }),
});
}