All files / src/components WelcomePage.jsx

0% Statements 0/31
0% Branches 0/19
0% Functions 0/12
0% Lines 0/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170                                                                                                                                                                                                                                                                                                                                                   
import React, { useState, useEffect } from 'react';
import { useLabState } from '../useLabState';
import { vscodeNative } from '../vscodeNative';
import {
  Atom, Key, FolderOpen, Rocket, FlaskConical, Search,
  BookOpen, RotateCcw, CheckCircle2, AlertCircle,
} from 'lucide-react';
 
const API_KEY_IDS = ['geminiApiKey', 'anthropicApiKey', 'openaiApiKey'];
 
const FEATURES = [
  { icon: FlaskConical, title: 'Simulate', desc: 'Generate interactive HTML simulations from natural language' },
  { icon: Search, title: 'Deep Research', desc: 'Launch long-running investigations with Google Deep Research' },
  { icon: BookOpen, title: 'Knowledge', desc: 'Build a searchable knowledge base across sessions' },
  { icon: RotateCcw, title: 'Auto Loop', desc: 'Let the agent ask its own questions and iterate autonomously' },
];
 
const LIBRARIES = [
  'math.js', 'Plotly', 'D3', 'Chart.js', 'Matter.js', 'Cannon-es',
  'Rapier', 'Turf.js', 'KaTeX', 'JSXGraph', 'gl-matrix', 'nerdamer',
];
 
function StepCard({ step, icon: Icon, title, desc, done, action, actionLabel }) {
  return (
    <div className={`rounded-2xl border p-4 transition-all ${done ? 'border-lab-accent/30 bg-lab-accent/5' : 'border-lab-border bg-lab-bg-secondary/50 hover:border-lab-border'}`}>
      <div className="flex items-start gap-3">
        <div className={`mt-0.5 flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center ${done ? 'bg-lab-accent/15 text-lab-accent' : 'bg-lab-bg-elevated text-lab-fg-secondary'}`}>
          <Icon className="w-4 h-4" />
        </div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 mb-1">
            <span className="text-[10px] font-bold uppercase tracking-wider text-lab-fg-muted">Step {step}</span>
            {done
              ? <span className="flex items-center gap-1 text-[10px] text-lab-accent"><CheckCircle2 className="w-3 h-3" /> Done</span>
              : <span className="flex items-center gap-1 text-[10px] text-lab-warning"><AlertCircle className="w-3 h-3" /> Needed</span>
            }
          </div>
          <h3 className="text-sm font-semibold text-lab-fg mb-1">{title}</h3>
          <p className="text-xs text-lab-fg-secondary leading-relaxed">{desc}</p>
          {action && !done && (
            <button
              onClick={action}
              className="mt-3 px-3 py-1.5 text-xs font-medium rounded-lg bg-lab-accent/10 text-lab-accent border border-lab-accent/20 hover:bg-lab-accent/20 transition-colors"
            >
              {actionLabel}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}
 
export default function WelcomePage({ onDismiss }) {
  const { setInput, workspaceFiles } = useLabState();
  const [hasApiKey, setHasApiKey] = useState(false);
  const [checking, setChecking] = useState(true);
 
  const hasWorkspace = workspaceFiles && workspaceFiles.length > 0;
 
  useEffect(() => {
    let cancelled = false;
    async function check() {
      for (const key of API_KEY_IDS) {
        const val = await vscodeNative.getSecret(key);
        if (val && !cancelled) { setHasApiKey(true); break; }
      }
      if (!cancelled) setChecking(false);
    }
    check();
    return () => { cancelled = true; };
  }, []);
 
  const tryPrompt = (text) => {
    setInput(text);
    onDismiss();
  };
 
  return (
    <div className="flex-1 flex items-start justify-center overflow-y-auto p-6 bg-lab-bg/50">
      <div className="w-full max-w-xl space-y-6 py-4">
 
        <div className="text-center space-y-3 mb-2">
          <div className="relative inline-block">
            <div className="absolute inset-0 bg-lab-accent/8 rounded-full blur-3xl scale-150" />
            <Atom className="w-16 h-16 text-lab-accent/20 animate-pulse relative" />
          </div>
          <h1 className="text-xl font-bold text-lab-fg tracking-wide">Welcome to ScienceLab</h1>
          <p className="text-xs text-lab-fg-secondary max-w-sm mx-auto leading-relaxed">
            AI-powered research environment. Describe a concept and get an interactive simulation — then iterate, build knowledge, and explore autonomously.
          </p>
        </div>
 
        {!checking && (
          <div className="space-y-3">
            <StepCard
              step={1} icon={Key}
              title="Configure an API Key"
              desc="Connect to Gemini, Claude, or OpenAI to power the research agent."
              done={hasApiKey}
              action={() => vscodeNative.executeCommand('sciencelab.configureApiKeys')}
              actionLabel="Configure API Keys"
            />
            <StepCard
              step={2} icon={FolderOpen}
              title="Open a Research Workspace"
              desc="Choose a folder to store your experiments, knowledge base, and journal."
              done={hasWorkspace}
              action={() => vscodeNative.executeCommand('sciencelab.openWorkspace')}
              actionLabel="Open Workspace"
            />
            <StepCard
              step={3} icon={Rocket}
              title="Start Researching"
              desc="Type a prompt below, or try one of these:"
              done={false}
              action={null}
            />
            <div className="flex flex-wrap gap-2 pl-11">
              {[
                'Simulate a double pendulum with adjustable mass and length',
                'Visualize the Mandelbrot set with zoom controls',
                'Model predator-prey population dynamics',
              ].map((prompt) => (
                <button
                  key={prompt}
                  onClick={() => tryPrompt(prompt)}
                  className="text-[11px] px-3 py-1.5 rounded-lg bg-lab-bg-secondary border border-lab-border text-lab-fg-secondary hover:text-lab-accent hover:border-lab-accent/30 transition-colors text-left"
                >
                  {prompt}
                </button>
              ))}
            </div>
          </div>
        )}
 
        <div className="border-t border-lab-border/50 pt-5 space-y-4">
          <h2 className="text-xs font-bold uppercase tracking-wider text-lab-fg-muted text-center">What you can do</h2>
          <div className="grid grid-cols-2 gap-2">
            {FEATURES.map(({ icon: FIcon, title, desc }) => (
              <div key={title} className="rounded-xl border border-lab-border/50 bg-lab-bg-secondary/30 p-3">
                <FIcon className="w-4 h-4 text-lab-accent/40 mb-2" />
                <h3 className="text-xs font-semibold text-lab-fg mb-0.5">{title}</h3>
                <p className="text-[10px] text-lab-fg-muted leading-relaxed">{desc}</p>
              </div>
            ))}
          </div>
        </div>
 
        <div className="text-center space-y-3 pb-2">
          <p className="text-[10px] text-lab-fg-muted">
            <span className="text-lab-fg-secondary font-medium">20+ libraries available: </span>
            {LIBRARIES.join(' · ')}
          </p>
          <button
            onClick={async () => {
              await vscodeNative.setSecret('welcomeDismissed', 'true');
              onDismiss();
            }}
            className="text-[10px] text-lab-fg-muted hover:text-lab-fg-secondary transition-colors underline underline-offset-2"
          >
            Don't show this again
          </button>
        </div>
 
      </div>
    </div>
  );
}