hydro-ai-helper 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,7 +97,7 @@ git clone https://gitee.com/alture/hydro-ai-helper.git # Gitee (mirror)
97
97
 
98
98
  cd hydro-ai-helper
99
99
  npm install
100
- npm run build
100
+ npm run build:plugin
101
101
 
102
102
  # Install into HydroOJ
103
103
  hydrooj addon add /path/to/hydro-ai-helper
@@ -6,7 +6,7 @@
6
6
 
7
7
  import React, { useEffect, useState, useCallback, useRef, useMemo } from 'react';
8
8
  import { i18n } from '@hydrooj/ui-default';
9
- import { COLORS, SPACING, RADIUS, SHADOWS, getButtonStyle, getAlertStyle, markdownTheme } from '../utils/styles';
9
+ import { COLORS, SPACING, RADIUS, SHADOWS, LAYOUT, getButtonStyle, getAlertStyle, markdownTheme } from '../utils/styles';
10
10
 
11
11
  /** i18n with hardcoded Chinese fallback for keys that may not yet be in lang-*.js */
12
12
  const I18N_FALLBACK: Record<string, string> = {
@@ -287,7 +287,7 @@ export const BatchSummaryPanel: React.FC<BatchSummaryPanelProps> = ({
287
287
  }
288
288
 
289
289
  return (
290
- <div style={{ fontFamily: 'inherit', color: COLORS.textPrimary }}>
290
+ <div style={{ fontFamily: 'inherit', color: COLORS.textPrimary, maxWidth: LAYOUT.contentMaxWidth, margin: '0 auto', width: '100%' }}>
291
291
  <style>{markdownTheme}</style>
292
292
 
293
293
  {isTeacher && (
@@ -6,7 +6,7 @@
6
6
 
7
7
  import React, { useEffect, useState, useRef, useCallback } from 'react';
8
8
  import { i18n } from '@hydrooj/ui-default';
9
- import { COLORS, SPACING, RADIUS, SHADOWS, markdownTheme } from '../utils/styles';
9
+ import { COLORS, SPACING, RADIUS, SHADOWS, LAYOUT, markdownTheme } from '../utils/styles';
10
10
  import { renderMarkdown } from '../utils/markdown';
11
11
 
12
12
  function t(key: string): string {
@@ -99,6 +99,9 @@ export const StudentSummaryView: React.FC<StudentSummaryViewProps> = ({ domainId
99
99
 
100
100
  return (
101
101
  <div style={{
102
+ maxWidth: LAYOUT.contentMaxWidth,
103
+ margin: '0 auto',
104
+ width: '100%',
102
105
  backgroundColor: COLORS.bgCard,
103
106
  borderRadius: RADIUS.md,
104
107
  boxShadow: SHADOWS.sm,
@@ -6,7 +6,7 @@
6
6
  import React, { useState, useEffect, useCallback } from 'react';
7
7
  import { i18n } from '@hydrooj/ui-default';
8
8
  import {
9
- COLORS, SPACING, RADIUS, SHADOWS,
9
+ COLORS, SPACING, RADIUS, SHADOWS, TRANSITIONS,
10
10
  getButtonStyle, cardStyle, markdownTheme, LAYOUT,
11
11
  } from '../utils/styles';
12
12
  import { renderMarkdown } from '../utils/markdown';
@@ -326,16 +326,7 @@ export const TeachingSummaryPanel: React.FC<TeachingSummaryPanelProps> = ({ doma
326
326
 
327
327
  const [teachingFocus, setTeachingFocus] = useState('');
328
328
  const [feedbackSubmitted, setFeedbackSubmitted] = useState(false);
329
-
330
- const [isWide, setIsWide] = useState(
331
- typeof window !== 'undefined' ? window.innerWidth >= LAYOUT.splitBreakpoint : true,
332
- );
333
-
334
- useEffect(() => {
335
- const handleResize = () => setIsWide(window.innerWidth >= LAYOUT.splitBreakpoint);
336
- window.addEventListener('resize', handleResize);
337
- return () => window.removeEventListener('resize', handleResize);
338
- }, []);
329
+ const [suggestionCollapsed, setSuggestionCollapsed] = useState(false);
339
330
 
340
331
  useEffect(() => {
341
332
  fetchSummary();
@@ -486,7 +477,7 @@ export const TeachingSummaryPanel: React.FC<TeachingSummaryPanelProps> = ({ doma
486
477
  : '';
487
478
 
488
479
  return (
489
- <div style={{ fontFamily: 'inherit', color: COLORS.textPrimary }}>
480
+ <div style={{ fontFamily: 'inherit', color: COLORS.textPrimary, maxWidth: LAYOUT.contentMaxWidth, margin: '0 auto', width: '100%' }}>
490
481
  <style>{markdownTheme}</style>
491
482
 
492
483
  {/* Panel header */}
@@ -548,19 +539,10 @@ export const TeachingSummaryPanel: React.FC<TeachingSummaryPanelProps> = ({ doma
548
539
  </div>
549
540
  )}
550
541
 
551
- {/* 60/40 split layout */}
552
- <style>{`.section { overflow: visible !important; }`}</style>
553
- <div style={{
554
- display: 'flex',
555
- flexDirection: isWide ? 'row' : 'column',
556
- gap: LAYOUT.gap,
557
- alignItems: 'flex-start',
558
- }}>
559
- {/* Main column (60%) — Findings */}
560
- <div style={{
561
- flex: isWide ? `0 0 ${LAYOUT.mainColumnWidth}` : '1 1 100%',
562
- minWidth: 0,
563
- }}>
542
+ {/* Single-column layout: Findings → AI Suggestion */}
543
+ <div>
544
+ {/* Findings */}
545
+ <div>
564
546
  <div style={{
565
547
  fontWeight: 600, fontSize: '13px', marginBottom: SPACING.md,
566
548
  color: COLORS.textMuted, textTransform: 'uppercase' as const,
@@ -588,16 +570,9 @@ export const TeachingSummaryPanel: React.FC<TeachingSummaryPanelProps> = ({ doma
588
570
  )}
589
571
  </div>
590
572
 
591
- {/* Sidebar (40%)AI Suggestion */}
573
+ {/* AI Suggestioncollapsible conclusion card */}
592
574
  {(summary.overallSuggestion || summary.status === 'generating') && (
593
- <div style={{
594
- flex: isWide ? `0 0 ${LAYOUT.sidebarWidth}` : '1 1 100%',
595
- minWidth: 0,
596
- position: isWide ? 'sticky' as const : 'static' as const,
597
- top: isWide ? LAYOUT.sidebarStickyTop : undefined,
598
- maxHeight: isWide ? LAYOUT.sidebarMaxHeight : undefined,
599
- overflowY: isWide ? 'auto' as const : undefined,
600
- }}>
575
+ <div style={{ marginTop: LAYOUT.sectionGap }}>
601
576
  <div style={{
602
577
  border: `1px solid ${COLORS.border}`,
603
578
  borderLeft: `4px solid ${COLORS.hydroGreen}`,
@@ -605,64 +580,83 @@ export const TeachingSummaryPanel: React.FC<TeachingSummaryPanelProps> = ({ doma
605
580
  backgroundColor: COLORS.bgCard,
606
581
  boxShadow: SHADOWS.sm,
607
582
  }}>
608
- {/* Header */}
609
- <div style={{
610
- padding: `${SPACING.md} ${SPACING.base}`,
611
- borderBottom: `1px solid ${COLORS.border}`,
612
- backgroundColor: COLORS.hydroGreenLight,
613
- }}>
583
+ {/* Clickable header */}
584
+ <div
585
+ onClick={() => setSuggestionCollapsed(!suggestionCollapsed)}
586
+ style={{
587
+ padding: `${SPACING.md} ${SPACING.base}`,
588
+ borderBottom: suggestionCollapsed ? 'none' : `1px solid ${COLORS.border}`,
589
+ backgroundColor: COLORS.hydroGreenLight,
590
+ cursor: 'pointer',
591
+ display: 'flex',
592
+ alignItems: 'center',
593
+ justifyContent: 'space-between',
594
+ userSelect: 'none' as const,
595
+ }}
596
+ >
614
597
  <span style={{
615
598
  fontWeight: 600, fontSize: '13px', color: COLORS.hydroGreenDark,
616
599
  textTransform: 'uppercase' as const, letterSpacing: '0.05em',
617
600
  }}>
618
601
  {t('ai_helper_teaching_summary_overall_suggestion')}
619
602
  </span>
603
+ <span style={{
604
+ display: 'inline-block', width: '16px', height: '16px',
605
+ textAlign: 'center' as const, lineHeight: '16px', fontSize: '12px',
606
+ color: COLORS.textMuted,
607
+ transition: `transform ${TRANSITIONS.fast}`,
608
+ transform: suggestionCollapsed ? 'rotate(0deg)' : 'rotate(90deg)',
609
+ }}>▶</span>
620
610
  </div>
621
611
 
622
- {/* Content */}
623
- {summary.overallSuggestion ? (
624
- <div
625
- className="markdown-body"
626
- style={{ padding: `${SPACING.base} ${SPACING.lg}` }}
627
- dangerouslySetInnerHTML={{ __html: renderMarkdown(summary.overallSuggestion) }}
628
- />
629
- ) : (
630
- <SkeletonBlock lines={10} />
612
+ {/* Collapsible content */}
613
+ {!suggestionCollapsed && (
614
+ <>
615
+ {summary.overallSuggestion ? (
616
+ <div
617
+ className="markdown-body"
618
+ style={{ padding: `${SPACING.base} ${SPACING.lg}` }}
619
+ dangerouslySetInnerHTML={{ __html: renderMarkdown(summary.overallSuggestion) }}
620
+ />
621
+ ) : (
622
+ <SkeletonBlock lines={10} />
623
+ )}
624
+
625
+ {/* Feedback */}
626
+ <div style={{
627
+ display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
628
+ gap: SPACING.sm, padding: `${SPACING.sm} ${SPACING.base}`,
629
+ borderTop: `1px solid ${COLORS.border}`,
630
+ }}>
631
+ {feedbackSubmitted ? (
632
+ <span style={{ fontSize: '12px', color: COLORS.successText }}>
633
+ {t('ai_helper_teaching_summary_feedback_thanks')}
634
+ </span>
635
+ ) : (
636
+ <>
637
+ {(['up', 'down'] as const).map(rating => (
638
+ <button
639
+ key={rating}
640
+ onClick={() => handleFeedback(rating)}
641
+ style={{
642
+ fontSize: '12px', padding: '3px 8px',
643
+ border: `1px solid ${COLORS.border}`,
644
+ borderRadius: RADIUS.sm,
645
+ backgroundColor: 'transparent',
646
+ color: summary.feedback?.rating === rating
647
+ ? (rating === 'up' ? COLORS.successText : COLORS.errorText)
648
+ : COLORS.textMuted,
649
+ cursor: 'pointer',
650
+ }}
651
+ >
652
+ {rating === 'up' ? t('ai_helper_teaching_summary_feedback_helpful') : t('ai_helper_teaching_summary_feedback_not_helpful')}
653
+ </button>
654
+ ))}
655
+ </>
656
+ )}
657
+ </div>
658
+ </>
631
659
  )}
632
-
633
- {/* Feedback — inside sidebar */}
634
- <div style={{
635
- display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
636
- gap: SPACING.sm, padding: `${SPACING.sm} ${SPACING.base}`,
637
- borderTop: `1px solid ${COLORS.border}`,
638
- }}>
639
- {feedbackSubmitted ? (
640
- <span style={{ fontSize: '12px', color: COLORS.successText }}>
641
- {t('ai_helper_teaching_summary_feedback_thanks')}
642
- </span>
643
- ) : (
644
- <>
645
- {(['up', 'down'] as const).map(rating => (
646
- <button
647
- key={rating}
648
- onClick={() => handleFeedback(rating)}
649
- style={{
650
- fontSize: '12px', padding: '3px 8px',
651
- border: `1px solid ${COLORS.border}`,
652
- borderRadius: RADIUS.sm,
653
- backgroundColor: 'transparent',
654
- color: summary.feedback?.rating === rating
655
- ? (rating === 'up' ? COLORS.successText : COLORS.errorText)
656
- : COLORS.textMuted,
657
- cursor: 'pointer',
658
- }}
659
- >
660
- {rating === 'up' ? t('ai_helper_teaching_summary_feedback_helpful') : t('ai_helper_teaching_summary_feedback_not_helpful')}
661
- </button>
662
- ))}
663
- </>
664
- )}
665
- </div>
666
660
  </div>
667
661
  </div>
668
662
  )}
@@ -320,11 +320,9 @@ export const linkStyle: React.CSSProperties = {
320
320
 
321
321
  export const LAYOUT = {
322
322
  splitBreakpoint: 768,
323
- mainColumnWidth: '58%',
324
- sidebarWidth: '40%',
325
- sidebarStickyTop: '16px',
326
- sidebarMaxHeight: 'calc(100vh - 32px)',
323
+ contentMaxWidth: '1080px',
327
324
  gap: '24px',
325
+ sectionGap: '32px',
328
326
  };
329
327
 
330
328
  // ─── Shared Markdown Theme ───────────────────────────────────────────────────
package/locales/en.yaml CHANGED
@@ -436,7 +436,7 @@ ai_helper_admin_version_updating: "Updating..."
436
436
  ai_helper_admin_version_update_success: "Update Successful"
437
437
  ai_helper_admin_version_update_failed_title: "Update Failed"
438
438
  ai_helper_admin_version_notice_title: "Important Notes"
439
- ai_helper_admin_version_notice_1: "Update will execute git pull, npm run build, and pm2 restart"
439
+ ai_helper_admin_version_notice_1: "Update will execute git pull, npm run build:plugin, and pm2 restart"
440
440
  ai_helper_admin_version_notice_2: "HydroOJ service will briefly restart, causing a few seconds of downtime"
441
441
  ai_helper_admin_version_notice_3: "Please ensure no critical operations are in progress"
442
442
  ai_helper_admin_version_plugin_path: "Plugin path"
package/locales/zh.yaml CHANGED
@@ -436,7 +436,7 @@ ai_helper_admin_version_updating: "正在更新..."
436
436
  ai_helper_admin_version_update_success: "更新成功"
437
437
  ai_helper_admin_version_update_failed_title: "更新失败"
438
438
  ai_helper_admin_version_notice_title: "注意事项"
439
- ai_helper_admin_version_notice_1: "更新将执行 git pull、npm run build 和 pm2 restart"
439
+ ai_helper_admin_version_notice_1: "更新将执行 git pull、npm run build:plugin 和 pm2 restart"
440
440
  ai_helper_admin_version_notice_2: "HydroOJ 服务将短暂重启,可能导致数秒服务中断"
441
441
  ai_helper_admin_version_notice_3: "请确保当前没有重要操作正在进行"
442
442
  ai_helper_admin_version_plugin_path: "插件路径"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hydro-ai-helper",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "HydroOJ AI Learning Assistant - 一个教学优先的 AI 辅助学习插件",
5
5
  "main": "dist/index.js",
6
6
  "author": "Alture",
@@ -22,7 +22,6 @@
22
22
  "learning-assistant"
23
23
  ],
24
24
  "scripts": {
25
- "build": "echo 'Skipping build for Vercel Functions'",
26
25
  "build:plugin": "tsc",
27
26
  "dev": "tsc --watch",
28
27
  "lint": "eslint src --ext .ts,.tsx",
@@ -49,7 +48,6 @@
49
48
  "@types/react-dom": "^17.0.0",
50
49
  "@typescript-eslint/eslint-plugin": "^6.0.0",
51
50
  "@typescript-eslint/parser": "^6.0.0",
52
- "@vercel/node": "^3.0.0",
53
51
  "eslint": "^8.0.0",
54
52
  "hydrooj": "^4.0.0",
55
53
  "jest": "^30.2.0",