Skip to content
Draft
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
66 changes: 66 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Verify Project

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
lint:
name: Lint & Format Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'

- name: Install npm dependencies
run: npm install

- name: Run ESLint
run: npm run lint

- name: Run Prettier check
run: npm run format:check

test:
name: Run Verification Tests
needs: lint
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install npm dependencies
run: npm install

- name: Install Playwright browsers
run: npx playwright install --with-deps

- name: Start local server
run: npm start &

- name: Wait for server to start
run: sleep 5 # Give server a moment to start

- name: Run Playwright tests
run: npm test
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Dependencies
/node_modules

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Build artifacts
/build
/dist

# System files
.DS_Store
Thumbs.db

# Verification artifacts
/verification_screenshots
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "always",
"printWidth": 80
}
43 changes: 43 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Agent Instructions for "react-component-dashboard"

This document outlines the specific rules and development patterns that must be followed when working on this project. This repository uses a non-standard, browser-only execution environment, so adherence to these rules is critical for success.

## 1. Environment Architecture

This project does **not** use a Node.js build system (like Webpack or Vite). All components are standalone React applications rendered by a single `index.html` file that loads dependencies directly from a CDN.

- **Dependencies are Global:** React, ReactDOM, and Babel are loaded from a CDN and exist as global variables (`window.React`, `window.ReactDOM`). Do not add `import` statements for them in component files.
- **Local Server is Required:** All testing and verification must be done by running a local web server (`npm start`). Direct `file://` access will not work due to browser security policies (CORS).

## 2. React Component Rules

All React components in this project **must** follow this specific structure to be compatible with the dashboard loader.

### Rule 2.1: No Module Imports/Exports

- **DO NOT** use `import React from 'react'`. React is already in the global scope.
- **DO NOT** use `export default App`. Components should not be ES modules.

### Rule 2.2: Qualify React Hooks

- All React hooks (`useState`, `useEffect`, `useRef`, `useCallback`, etc.) **must** be explicitly called as properties of the global `React` object.
- **Correct:** `const [myState, setMyState] = React.useState('');`
- **Incorrect:** `const [myState, setMyState] = useState('');`

### Rule 2.3: Global Component Assignment

- Each main component function **must** be assigned to a unique property on the `window` object. This is how the central dashboard finds and renders it.
- The variable name should be descriptive (e.g., `window.MyAwesomeComponentApp`).
- **Correct:** `window.MyAwesomeComponentApp = function App() { ... };`
- **Incorrect:** `function App() { ... };` or `const App = () => { ... };`

### Rule 2.4: No Self-Rendering

- Components **must not** render themselves. The `index.html` dashboard is responsible for all rendering logic (`ReactDOM.createRoot(...).render(...)`).
- **DO NOT** include `ReactDOM.createRoot` or `root.render` calls at the end of a component file.

## 3. Verification and Testing

- Any change to a component's UI **must** be verified using the comprehensive Playwright script located at `jules-scratch/verification/verify_all.py`.
- If you add a new component, you **must** update this verification script to include a test for it. The test should select the new component from the dashboard dropdown and wait for a unique, stable selector to confirm it has rendered correctly.
- All verification tests **must** pass before submitting code.
20 changes: 10 additions & 10 deletions DangerousCodeTest.txt → DangerousCodeTest.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react';
// React is loaded globally from the CDN.

// The default payload is the user's provided code.
const defaultDangerousSnippet = `
Expand Down Expand Up @@ -65,20 +65,20 @@ try {
`;

// --- The Main Harness Application ---
const App = () => {
const [lastCrashReport, setLastCrashReport] = useState(null);
const [log, setLog] = useState([]);
const [isRunning, setIsRunning] = useState(false);
const [testSnippet, setTestSnippet] = useState(defaultDangerousSnippet.trim());
const [copySuccess, setCopySuccess] = useState('');
const iframeRef = useRef(null);
window.DangerousCodeTestApp = function App() {
const [lastCrashReport, setLastCrashReport] = React.useState(null);
const [log, setLog] = React.useState([]);
const [isRunning, setIsRunning] = React.useState(false);
const [testSnippet, setTestSnippet] = React.useState(defaultDangerousSnippet.trim());
const [copySuccess, setCopySuccess] = React.useState('');
const iframeRef = React.useRef(null);

const addLog = (message, status = 'INFO', data = null) => {
const timestamp = new Date().toISOString().split('T')[1].slice(0, -1);
setLog(prev => [{ id: Date.now() + Math.random(), timestamp, status, message, data }, ...prev]);
};

useEffect(() => {
React.useEffect(() => {
const handleMessage = (event) => {
if (iframeRef.current && event.source !== iframeRef.current.contentWindow) return;

Expand Down Expand Up @@ -269,4 +269,4 @@ const App = () => {
);
};

export default App;
// No export needed, component is assigned to window
16 changes: 8 additions & 8 deletions GeminiAppCanvasAgent.txt → GeminiAppCanvasAgent.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react';
// React is loaded globally from the CDN.

// --- CLI Internals ---
// All command logic and helpers are moved to a global object
Expand Down Expand Up @@ -360,18 +360,18 @@ Respond with ONLY a JSON object in the format: {"plan": ["command_1", "command_2
};

// Main App Component
export default function App() {
const [history, setHistory] = useState([
window.GeminiAppCanvasAgentApp = function App() {
const [history, setHistory] = React.useState([
{
type: 'system',
output: 'G\'day! The agent is now fully autonomous and thinks in plans. Give it a burl with `agent <your_goal>`.',
},
]);
const [command, setCommand] = useState('');
const terminalRef = useRef(null);
const appRootRef = useRef(null);
const [command, setCommand] = React.useState('');
const terminalRef = React.useRef(null);
const appRootRef = React.useRef(null);

useEffect(() => {
React.useEffect(() => {
window.cli_internals.initDB();
const originalConsole = { ...console };
const consoleMethods = ['log', 'warn', 'error', 'info'];
Expand All @@ -394,7 +394,7 @@ export default function App() {
if (e.key === 'Enter') { e.preventDefault(); processCommand(command); setCommand(''); }
};

useEffect(() => { terminalRef.current?.scrollTo(0, terminalRef.current.scrollHeight); }, [history]);
React.useEffect(() => { terminalRef.current?.scrollTo(0, terminalRef.current.scrollHeight); }, [history]);

const renderHistoryEntry = (entry, index) => {
const entryTypes = {
Expand Down
8 changes: 2 additions & 6 deletions GeminiAppCanvasCLI.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// React is loaded globally from the CDN, so no import is needed.

// Main App Component
function App() {
window.GeminiAppCanvasCLIApp = function App() {
const [history, setHistory] = React.useState([
{
command: 'welcome',
Expand Down Expand Up @@ -303,8 +303,4 @@ function App() {
);
}

// --- Self-Rendering Logic ---
// This code will be executed after the App function is defined.
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(React.createElement(App));
// No self-rendering logic needed. The dashboard handles rendering.
18 changes: 9 additions & 9 deletions GeminiAppJavascriptIntrospector.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { useState, useCallback } from 'react';
// React is loaded globally from the CDN.

// Main App component for the JavaScript Introspector
function App() {
window.GeminiAppJavascriptIntrospectorApp = function App() {
// State for the JavaScript code input by the user
const [jsCode, setJsCode] = useState('');
const [jsCode, setJsCode] = React.useState('');
// State for the introspection results (functions and variables found from code input)
const [introspectionResult, setIntrospectionResult] = useState(null);
const [introspectionResult, setIntrospectionResult] = React.useState(null);
// State for the environment introspection results
const [envIntrospectionResult, setEnvIntrospectionResult] = useState(null);
const [envIntrospectionResult, setEnvIntrospectionResult] = React.useState(null);
// State for the current active tab in the meta-data panel ('devlog', 'changelog', 'about', 'known-bugs', 'environment')
const [activeMetaTab, setActiveMetaTab] = useState('about');
const [activeMetaTab, setActiveMetaTab] = React.useState('about');
// Hardcoded semantic version for the application - Patch version bump for improved error handling/guidance
const appVersion = "2.3.2"; // Incremented for improved error handling/guidance

Expand Down Expand Up @@ -242,7 +242,7 @@ function App() {
* Copies the introspection results (functions and variables) to the clipboard as a JSON string.
* Provides user feedback via a temporary message box.
*/
const copyResultsToClipboard = useCallback(() => {
const copyResultsToClipboard = React.useCallback(() => {
// Determine which results to copy based on the active tab, or provide a default
const resultsToCopy = activeMetaTab === 'environment' && envIntrospectionResult
? envIntrospectionResult
Expand Down Expand Up @@ -284,7 +284,7 @@ function App() {
* Downloads the introspection results as a JSON file.
* The filename is `js-introspector-results-<timestamp>.json`.
*/
const downloadResults = useCallback(() => {
const downloadResults = React.useCallback(() => {
// Determine which results to download based on the active tab, or provide a default
const resultsToDownload = activeMetaTab === 'environment' && envIntrospectionResult
? envIntrospectionResult
Expand Down Expand Up @@ -896,4 +896,4 @@ function App() {
);
}

export default App;
// No export needed, component is assigned to window
48 changes: 24 additions & 24 deletions GeminiAppProbeReactApp.txt → GeminiAppProbeReactApp.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
// React is loaded globally from the CDN.

// --- Firebase Imports (dynamically loaded for robustness) ---
let initializeApp, getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged,
Expand Down Expand Up @@ -26,8 +26,8 @@ const decodeJwt = (token) => {
};

const useConsoleCapture = () => {
const [consoleOutput, setConsoleOutput] = useState([]);
useEffect(() => {
const [consoleOutput, setConsoleOutput] = React.useState([]);
React.useEffect(() => {
const originalConsole = { log: console.log, warn: console.warn, error: console.error, info: console.info, debug: console.debug };
const formatArgs = (args) => Array.from(args).map(arg => {
try {
Expand Down Expand Up @@ -66,24 +66,24 @@ const PlayCircleIcon = ({ className }) => (<svg xmlns="http://www.w3.org/2000/sv
const ShieldIcon = ({ className }) => (<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>);

// --- Main App Component ---
export default function App() {
window.GeminiAppProbeReactAppApp = function App() {
const [consoleOutput, setConsoleOutput] = useConsoleCapture();
const [activeTab, setActiveTab] = useState('environment');
const [isLoading, setIsLoading] = useState(false);
const [isSuiteRunning, setIsSuiteRunning] = useState(false);
const [includeGeminiAnalysis, setIncludeGeminiAnalysis] = useState(true);
const [activeTab, setActiveTab] = React.useState('environment');
const [isLoading, setIsLoading] = React.useState(false);
const [isSuiteRunning, setIsSuiteRunning] = React.useState(false);
const [includeGeminiAnalysis, setIncludeGeminiAnalysis] = React.useState(true);

// --- All Test States ---
const [introspectionResult, setIntrospectionResult] = useState({});
const [apiResults, setApiResults] = useState({});
const [geminiApiResponse, setGeminiApiResponse] = useState(null);
const [geminiPrompt, setGeminiPrompt] = useState('Please analyze the following JSON log from a comprehensive web environment prober. Summarize the key findings, capabilities, and limitations.');
const [firestoreState, setFirestoreState] = useState({ status: 'uninitialized', userId: null, error: null, entries: [] });
const [newMessage, setNewMessage] = useState('');
const [urlToFetch, setUrlToFetch] = useState('https://dbpedia.org/sparql');
const [privateData, setPrivateData] = useState(null);
const [introspectionResult, setIntrospectionResult] = React.useState({});
const [apiResults, setApiResults] = React.useState({});
const [geminiApiResponse, setGeminiApiResponse] = React.useState(null);
const [geminiPrompt, setGeminiPrompt] = React.useState('Please analyze the following JSON log from a comprehensive web environment prober. Summarize the key findings, capabilities, and limitations.');
const [firestoreState, setFirestoreState] = React.useState({ status: 'uninitialized', userId: null, error: null, entries: [] });
const [newMessage, setNewMessage] = React.useState('');
const [urlToFetch, setUrlToFetch] = React.useState('https://dbpedia.org/sparql');
const [privateData, setPrivateData] = React.useState(null);

const firebaseApp = useRef(null);
const firebaseApp = React.useRef(null);


const GEMINI_API_KEY = ""; // Keep blank per Canvas instructions
Expand Down Expand Up @@ -116,7 +116,7 @@ export default function App() {
};

// --- Introspection Logic ---
const runIntrospection = useCallback(() => {
const runIntrospection = React.useCallback(() => {
const { __app_id, __firebase_config, __initial_auth_token } = typeof window !== 'undefined' ? window : {};
const decodedToken = __initial_auth_token ? decodeJwt(__initial_auth_token) : 'N/A';
setIntrospectionResult({
Expand All @@ -129,7 +129,7 @@ export default function App() {
});
}, []);

useEffect(() => {
React.useEffect(() => {
runIntrospection();
}, [runIntrospection]);

Expand Down Expand Up @@ -363,7 +363,7 @@ export default function App() {
};

// --- Firestore Logic ---
const initFirestore = useCallback(async () => {
const initFirestore = React.useCallback(async () => {
if (firebaseApp.current) {
const auth = getAuth(firebaseApp.current);
if (auth.currentUser) {
Expand Down Expand Up @@ -416,11 +416,11 @@ export default function App() {
}
}, []);

useEffect(() => {
React.useEffect(() => {
initFirestore();
}, [initFirestore]);

useEffect(() => {
React.useEffect(() => {
if (firestoreState.status.startsWith('authenticated') && firebaseApp.current) {
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
const db = getFirestore(firebaseApp.current);
Expand Down Expand Up @@ -517,8 +517,8 @@ export default function App() {

// --- Panel Renderers ---
const InfoCard = ({ title, children, status, copyText }) => {
const [isCopied, setIsCopied] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
const [isCopied, setIsCopied] = React.useState(false);
const [isExpanded, setIsExpanded] = React.useState(false);
const statusColor = status === 'success' ? 'border-green-500' : status === 'error' ? 'border-red-500' : status === 'warning' ? 'border-yellow-500' : 'border-slate-600';
const content = (typeof children === 'object' && children !== null) ? JSON.stringify(children, null, 2) : String(children);
const canExpand = content.length > 150;
Expand Down
Loading