Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,9 @@
"*.{ts,tsx,js,jsx,json,md,css,html,yml,yaml}": [
"prettier --check"
]
},
"allowScripts": {
"chromedriver@149.0.4": true,
"esbuild@0.28.1": true
}
}
20 changes: 13 additions & 7 deletions src/components/LanguageLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,31 @@ function JavaLogo({ size }: { size: number }) {
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} viewBox="0 0 32 32">
<path
d="M11.622 24.74s-1.23.748.855.962c2.51.32 3.847.267 6.625-.267a10.02 10.02 0 0 0 1.763.855c-6.25 2.672-14.16-.16-9.244-1.55zm-.8-3.473s-1.336 1.015.748 1.23c2.725.267 4.862.32 8.55-.427a3.26 3.26 0 0 0 1.282.801c-7.534 2.244-15.976.214-10.58-1.603zm14.747 6.09s.908.748-1.015 1.336c-3.58 1.07-15.014 1.39-18.22 0-1.122-.48 1.015-1.175 1.7-1.282.695-.16 1.07-.16 1.07-.16-1.23-.855-8.175 1.763-3.526 2.51 12.77 2.084 23.296-.908 19.983-2.404zM12.2 17.633s-5.824 1.39-2.084 1.87c1.603.214 4.755.16 7.694-.053 2.404-.214 4.81-.64 4.81-.64s-.855.374-1.443.748c-5.93 1.55-17.312.855-14.052-.748 2.778-1.336 5.076-1.175 5.076-1.175zm10.42 5.824c5.984-3.1 3.206-6.09 1.282-5.717-.48.107-.695.214-.695.214s.16-.32.534-.427c3.794-1.336 6.786 4.007-1.23 6.09 0 0 .053-.053.107-.16zm-9.83 8.442c5.77.374 14.587-.214 14.8-2.94 0 0-.427 1.07-4.755 1.87-4.916.908-11.007.8-14.587.214 0 0 .748.64 4.542.855z"
fill="#4e7896"
fill="#2ba6e0"
/>
<path
d="M18.996.001s3.313 3.366-3.152 8.442c-5.183 4.114-1.175 6.465 0 9.137-3.046-2.725-5.236-5.13-3.74-7.373C14.294 6.893 20.332 5.3 18.996.001zm-1.7 15.335c1.55 1.763-.427 3.366-.427 3.366s3.954-2.03 2.137-4.542c-1.656-2.404-2.94-3.58 4.007-7.587 0 0-10.953 2.725-5.717 8.763z"
fill="#f58219"
fill="#ea2d2e"
/>
</svg>
);
}

function CspLogo({ size }: { size: number }) {
// A stylized acorn — the mark used across College Board's AP Program
// branding (including AP CSP) — rendered in the current theme color.
return (
<img
src="https://resources.finalsite.net/images/f_auto,q_auto/v1641237113/southmontk12inus/lykadnt0znr0d1vvodlp/101-1011446_teen-center-sat-600600-college-board-acorn.png"
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
alt="CSP"
style={{ objectFit: 'contain' }}
/>
viewBox="0 0 463.33334 463.33334"
>
<path
d="m 223.74629,454.45444 c -5.33702,-1.93672 -19.29754,-11.79109 -31.01573,-21.89321 -3.3,-2.84489 -9.57387,-8.05891 -13.94193,-11.58669 -4.36805,-3.5278 -12.80291,-11.22244 -18.74412,-17.0992 C 128.2928,372.468 104.95849,334.66342 92.5042,294.45131 c -3.93696,-12.71154 -7.05085,-27.90184 -7.78677,-37.98564 -0.58325,-7.99198 -1.30232,-9.9838 -5.02548,-13.92064 -6.81557,-7.20674 -17.52612,-25.19721 -22.63588,-38.0214 -5.35122,-13.4302 -6.91617,-21.84474 -6.50937,-35 0.2669,-8.63095 0.52288,-10.32542 2.38921,-15.81501 4.16078,-12.23848 8.49914,-19.15893 17.35522,-27.6846 13.3648,-12.86618 30.81056,-21.23771 58.66474,-28.150898 16.5832,-4.11582 29.58066,-6.28136 55.87741,-9.309874 4.82317,-0.555466 9.54725,-1.364353 10.49795,-1.797526 2.13546,-0.97298 3.0572,-3.973574 3.06228,-9.968794 0.0141,-16.645363 12.36428,-36.477536 31.79604,-51.058671 13.82867,-10.376704 30.45741,-18.8961892 34.71125,-17.783785 6.34811,1.6600687 7.64009,30.424676 2.13985,47.6419 -1.64635,5.153521 -6.09309,13.552043 -9.38892,17.732776 -10.37822,13.16468 -10.40805,13.24552 -5.08784,13.79018 1.925,0.197067 10.1,0.982227 18.16667,1.744794 34.12749,3.22618 55.40061,6.681186 74.24715,12.058618 35.44111,10.11232 56.86297,27.23303 64.75867,51.75625 6.09618,18.93408 3.281,38.89943 -8.86042,62.8385 -5.19006,10.23314 -11.08949,19.33414 -17.36208,26.78431 -4.23625,5.03153 -4.3105,5.18799 -4.74549,10 -4.54334,50.25964 -23.11343,92.85399 -58.08144,133.22183 -12.27286,14.16806 -23.68154,25.14785 -42.96814,41.35286 -22.21426,18.66489 -27.42036,22.53689 -35.19162,26.17351 -5.49795,2.5728 -13.8418,3.19675 -18.7809,1.40444 z m 16.33323,-19.55612 c 13.31442,-6.03704 39.01738,-26.58809 51.31771,-41.03156 18.34824,-21.54514 31.68591,-41.40052 42.20184,-62.82457 17.23366,-35.11004 21.51636,-55.1144 16.80989,-78.51856 -2.73919,-13.62141 -1.11877,-21.92993 7.76592,-39.8187 1.95368,-3.93362 4.79044,-9.17517 6.30391,-11.64789 11.79502,-19.27079 14.04747,-34.07268 7.28403,-47.86675 -7.82917,-15.96761 -27.01676,-27.50423 -59.17191,-35.57735 -22.96469,-5.7657 -39.65271,-8.23967 -64.465,-9.55684 -21.87166,-1.16107 -40.78528,0.14903 -65.39535,4.52978 -29.24555,5.20589 -47.55597,12.72879 -59.01131,24.24498 -7.83948,7.88113 -11.55068,16.86014 -11.58372,28.0261 -0.0594,20.06274 12.64841,31.63818 37.29277,33.96977 6.68699,0.63265 11.61419,0.41202 35.96893,-1.61062 37.76927,-3.13671 74.0308,-3.11541 93.9737,0.0552 17.83394,2.8353 35.0263,10.68436 35.0263,15.99102 0,4.25047 -2.57238,4.94364 -11.03778,2.97432 -16.69833,-3.88454 -39.01871,-5.31679 -63.9553,-4.10386 -9.16286,0.44569 -19.21286,1.25008 -22.33334,1.78754 -23.39244,4.029 -44.8716,14.49415 -55.09946,26.84574 -6.32068,7.63312 -8.96532,14.40998 -11.5171,29.51233 -6.78276,40.14291 5.30661,83.08078 35.45111,125.9119 7.6417,10.85779 11.80034,15.94604 19.81103,24.23955 8.30053,8.59357 14.05354,12.88266 19.99889,14.9099 5.92987,2.02197 9.14957,1.92302 14.36424,-0.44143 z M 228.14065,78.081581 c 7.20401,-5.401533 14.53571,-14.615766 19.06998,-23.966511 3.23212,-6.665388 7.1866,-18.183781 7.1866,-20.932755 0,-4.931442 -9.47777,1.667682 -17.57056,12.233927 -8.15636,10.649242 -14.35638,26.328626 -12.82449,32.432173 0.51559,2.05426 1.62624,2.116833 4.13847,0.233166 z"
fill="currentColor"
/>
</svg>
);
}

Expand Down
5 changes: 4 additions & 1 deletion src/components/OutputPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type OutputPanelState = 'open' | 'closed';
interface OutputPanelProps {
output: string[];
error?: string | null;
/** False until the user's first Run/Debug — suppresses the placeholder after that. */
hasRun?: boolean;
variables?: Record<string, any>;
showVariables?: boolean;
height?: number;
Expand Down Expand Up @@ -44,6 +46,7 @@ const formatVariableValue = (value: any): string => {
export const OutputPanel: React.FC<OutputPanelProps> = ({
output,
error,
hasRun = false,
variables = {},
showVariables = false,
height,
Expand Down Expand Up @@ -150,7 +153,7 @@ export const OutputPanel: React.FC<OutputPanelProps> = ({
aria-label="Program output"
aria-atomic="false"
>
{output.length === 0 && !error ? (
{!hasRun && output.length === 0 && !error ? (
<div className="text-slate-400 italic">Run code to see execution results...</div>
) : (
output.map((line, idx) => {
Expand Down
134 changes: 41 additions & 93 deletions src/components/editor/AddPanelStrip.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import { Plus, FileJson, Check, Bot } from 'lucide-react';
import type { MouseEvent } from 'react';
import { FileJson, Bot, BrainCircuit } from 'lucide-react';

import { LANG_LABELS, type SupportedLang } from '../LanguageSelector';
import { LanguageLogo } from '../LanguageLogo';
import type { Panel } from './types';

interface AddPanelStripProps {
showAddMenu: boolean;
sourceLang: SupportedLang;
panels: Panel[];
showAiSidePanel: boolean;
/** True while the AI panel is being resized (highlights the handle). */
aiResizeActive: boolean;
onToggleMenu: () => void;
showMemDia: boolean;
onTogglePanel: (lang: SupportedLang) => void;
onToggleAiPanel: () => void;
/** Starts an AI-panel resize drag from this strip's left edge. */
onStartAiResize: (e: MouseEvent) => void;
onToggleMemDia: () => void;
}

const PANEL_LANGS: SupportedLang[] = [
Expand All @@ -29,107 +23,61 @@ const PANEL_LANGS: SupportedLang[] = [
'python',
];

const toggleButtonClasses = (active: boolean) =>
`p-3 rounded-xl transition-all shadow-lg active:scale-90 border ${
active
? 'bg-indigo-600 text-white border-indigo-500'
: 'bg-slate-800 hover:bg-indigo-600 text-indigo-400 hover:text-white border-slate-700'
}`;

export function AddPanelStrip({
showAddMenu,
sourceLang,
panels,
showAiSidePanel,
aiResizeActive,
onToggleMenu,
showMemDia,
onTogglePanel,
onToggleAiPanel,
onStartAiResize,
onToggleMemDia,
}: AddPanelStripProps) {
return (
<div className="add-panel-dropdown w-16 flex flex-col items-center gap-3 pt-4 bg-slate-900 border-l border-slate-800 shrink-0 relative z-[150] shadow-[-10px_0_20px_rgba(0,0,0,0.5)]">
{/* The strip sits between the code panes and the AI panel, so its left
edge doubles as a second resize handle for the AI panel. */}
{showAiSidePanel && (
<div
className={`absolute top-0 left-0 w-1 h-full cursor-col-resize z-[200] transition-colors ${
aiResizeActive ? 'bg-indigo-500' : 'bg-transparent hover:bg-indigo-500/40'
}`}
onMouseDown={onStartAiResize}
aria-hidden="true"
/>
)}
<div className="relative">
<button
onClick={onToggleMenu}
className="p-3 bg-slate-800 hover:bg-indigo-600 rounded-xl text-indigo-400 hover:text-white transition-all shadow-lg active:scale-90 border border-slate-700"
title="Add Translation View"
>
<Plus size={24} />
</button>
<div className="add-panel-dropdown w-16 h-full flex flex-col items-center gap-3 pt-4 overflow-y-auto bg-slate-900 border-r border-slate-800 shrink-0 relative z-[150] shadow-[10px_0_20px_rgba(0,0,0,0.5)]">
{PANEL_LANGS.map((lang) => {
const isOpen = panels.some((panel) => panel.lang === lang);
const label = LANG_LABELS[lang];

{showAddMenu && (
<div className="absolute top-0 right-full mr-3 w-48 bg-slate-800 border border-slate-700 rounded-lg shadow-[0_10px_40px_rgba(0,0,0,0.7)] overflow-hidden z-[999] animate-in fade-in slide-in-from-right-2 duration-200">
<div className="p-3 text-[11px] font-bold text-slate-400 border-b border-slate-700 bg-slate-900/50 uppercase tracking-widest">
Open View
</div>
<div className="p-1">
{PANEL_LANGS.map((lang) => {
const isOpen = panels.some((panel) => panel.lang === lang);
const isSourceLanguage = lang === sourceLang;
const isDisabled = isSourceLanguage;
return (
<button
key={lang}
onClick={() => onTogglePanel(lang)}
aria-pressed={isOpen}
className={toggleButtonClasses(isOpen)}
title={label}
>
{lang === 'ast' ? <FileJson size={24} /> : <LanguageLogo lang={lang} size={24} />}
</button>
);
})}

return (
<button
key={lang}
onClick={() => !isDisabled && onTogglePanel(lang)}
disabled={isDisabled}
className={`flex items-center justify-between gap-3 w-full text-left px-3 py-2.5 text-xs rounded-md transition-colors group ${
isDisabled
? 'text-slate-600 cursor-not-allowed opacity-50'
: isOpen
? 'text-emerald-300 hover:bg-slate-700'
: 'text-slate-300 hover:bg-indigo-600 hover:text-white'
}`}
>
<span className="flex items-center gap-2.5">
{lang === 'ast' ? (
<FileJson
size={14}
className="opacity-60 group-hover:opacity-100 shrink-0"
/>
) : (
<span className="shrink-0">
<LanguageLogo lang={lang} size={14} />
</span>
)}
{LANG_LABELS[lang]}
</span>
<span className="flex items-center gap-1.5 shrink-0">
{isSourceLanguage && (
<span className="text-[10px] uppercase tracking-wide text-slate-500">
Source
</span>
)}
{isOpen && !isSourceLanguage && (
<Check size={12} className="text-emerald-400" />
)}
</span>
</button>
);
})}
</div>
</div>
)}
</div>
<div className="w-8 h-px bg-slate-700 shrink-0" aria-hidden="true" />

{/* AI assistant — opens the side chat */}
<button
onClick={onToggleAiPanel}
aria-pressed={showAiSidePanel}
className={`p-3 rounded-xl transition-all shadow-lg active:scale-90 border ${
showAiSidePanel
? 'bg-indigo-600 text-white border-indigo-500'
: 'bg-slate-800 hover:bg-indigo-600 text-indigo-400 hover:text-white border-slate-700'
}`}
title={showAiSidePanel ? 'Close AI Assistant' : 'Open AI Assistant'}
className={toggleButtonClasses(showAiSidePanel)}
title="AI Assistant"
>
<Bot size={24} />
</button>

{/* Memory Diagram — shows live variable state alongside each pane */}
<button
onClick={onToggleMemDia}
aria-pressed={showMemDia}
className={toggleButtonClasses(showMemDia)}
title="Memory Diagram"
>
<BrainCircuit size={24} />
</button>
</div>
);
}
27 changes: 1 addition & 26 deletions src/components/editor/EditorHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ interface EditorHeaderProps {
embedCopied: boolean;
showExamplesMenu: boolean;
showSettingsMenu: boolean;
showMemDia: boolean;
isDebugging: boolean;
isDebugComplete: boolean;
examples: ExampleProgram[];
Expand All @@ -34,7 +33,6 @@ interface EditorHeaderProps {
onLoadExample: (exampleId: string) => void;
onToggleExamplesMenu: () => void;
onToggleSettingsMenu: () => void;
onToggleMemDia: () => void;
onDebugStart: () => void;
onRun: () => void;
onDebugStep: () => void;
Expand All @@ -49,7 +47,6 @@ export function EditorHeader({
embedCopied,
showExamplesMenu,
showSettingsMenu,
showMemDia,
isDebugging,
isDebugComplete,
examples,
Expand All @@ -59,7 +56,6 @@ export function EditorHeader({
onLoadExample,
onToggleExamplesMenu,
onToggleSettingsMenu,
onToggleMemDia,
onDebugStart,
onRun,
onDebugStep,
Expand Down Expand Up @@ -187,29 +183,8 @@ export function EditorHeader({
aria-label="Editor settings"
className="absolute top-full right-0 mt-2 w-64 bg-slate-900 border border-slate-700 rounded-lg shadow-xl z-[220]"
>
{/* Editor Panels section */}
<div className="px-4 py-3 border-b border-slate-800 text-[11px] font-bold uppercase tracking-widest text-slate-400">
Editor Panels
</div>
<div className="p-2">
<button
onClick={onToggleMemDia}
role="switch"
aria-checked={showMemDia}
className={`w-full flex items-center justify-between px-3 py-2 rounded-md text-left hover:bg-slate-800 transition-colors ${focusRing}`}
>
<span className="text-sm text-slate-200">Memory Diagram (MemDia)</span>
<span
className={`text-[11px] font-bold uppercase tracking-wide ${showMemDia ? 'text-emerald-400' : 'text-slate-500'}`}
aria-hidden="true"
>
{showMemDia ? 'On' : 'Off'}
</span>
</button>
</div>

{/* Text Size slider */}
<div className="px-4 py-3 border-t border-slate-800 text-[11px] font-bold uppercase tracking-widest text-slate-400">
<div className="px-4 py-3 border-b border-slate-800 text-[11px] font-bold uppercase tracking-widest text-slate-400">
Display
</div>
<div className="px-4 pb-4">
Expand Down
Loading
Loading