Skip to content

Commit cf7cc40

Browse files
committed
README and test run output issue
1 parent ab788b2 commit cf7cc40

2 files changed

Lines changed: 157 additions & 2 deletions

File tree

README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# PATTERN_ — Deep Work Interview Prep IDE
2+
3+
A single-page interview prep tool designed for deliberate practice. Works standalone in the browser (no server required) — or with an optional Flask backend for Python/Java execution.
4+
5+
Built to embed as an iframe on a blog. Users bring their own Gemini API key.
6+
7+
![Browser-Only](https://img.shields.io/badge/mode-browser--only-blue) ![Optional Server](https://img.shields.io/badge/server-optional-gray)
8+
9+
---
10+
11+
## Core Workflow
12+
13+
The app enforces a structured problem-solving workflow across four steps before writing code:
14+
15+
1. **Absorption & Constraints** — restate the problem, identify inputs/outputs/edge cases
16+
2. **Pattern Hunt & Data Structures** — identify applicable patterns and data structures
17+
3. **Logical Flow (Pseudocode)** — write pseudocode for the approach
18+
4. **Dry Run & Edge Cases** — trace through examples manually
19+
20+
Each step has a Monaco editor and an **AI Validate** button that sends your work to Gemini for feedback.
21+
22+
After the four steps, the **Implementation** section provides a full code editor with run/test capabilities.
23+
24+
---
25+
26+
## Features
27+
28+
### Problem Management
29+
- **LeetCode Catalog Search** — search and load problems from a cached LeetCode problem list
30+
- **Custom Problems** — paste any problem statement directly
31+
- **LC Seed Paste (📋)** — paste raw LeetCode API JSON blob to populate the problem catalog into localStorage
32+
- **Multi-Tab Workspace** — open multiple problems as tabs, each with independent state
33+
- **Tab Close vs Delete** — closing a tab hides it; deleting (🗑 in sidebar) permanently removes with confirmation
34+
- **Rename/Delete** — hover sidebar items to rename (✎) or delete (🗑)
35+
36+
### AI Integration (Gemini)
37+
- **Collapsible API Key Input**`{🗝}` trigger in sidebar footer, animated border pulse when no key is set, green when configured
38+
- **Step Validation** — each workbook step can be validated against the problem statement
39+
- **AI Strategy Analysis** — full pattern/approach analysis in the Reference view
40+
- **Boilerplate Generation** — auto-generates language-appropriate starter code on problem init
41+
- **Test Generation** — generates unit tests from your implementation code
42+
- **Auto Test Gen Timer** — after a successful code run, a 9-second countdown triggers silent background test generation (if tests editor is empty). Cancelable with Escape or click.
43+
- **Parallel Call Prevention (genMgr)** — prevents concurrent Gemini calls. All `[data-genaction]` buttons disable/dim during active calls. Dedup keys prevent re-running identical requests.
44+
- **Contextual Chat** — select any text in editors, description, or AI analysis to open a contextual question popup
45+
- **Chat Threads** — threaded Q&A with `@hash` cross-referencing between threads
46+
- **Ad-Hoc Chat** — general questions with code context (800 chars) and last 3 thread Q&As included automatically
47+
- **Gemini Proxy** — calls go through server when available, direct to Gemini API when in browser-only mode
48+
49+
### Code Execution
50+
- **JavaScript** — sandboxed execution in a hidden iframe (no server needed, 10s timeout)
51+
- **Python / Java** — server-side execution via Flask backend
52+
- **Run Logic** — executes the code editor contents
53+
- **Run Tests** — concatenates code + tests editors and executes together
54+
- **Sanity Check** — client-side static analysis blocks dangerous patterns (`eval`, `subprocess`, `__import__`, etc.). Server-side AI check for Python/Java.
55+
- **Safe Code Hash Cache** — previously approved code skips re-checking
56+
57+
### Voice (STT / TTS)
58+
- **Speech-to-Text (🎤)** — continuous speech recognition via Web Speech API
59+
- **Text-to-Speech (🔇/🔊)** — reads AI responses aloud, strips markdown, chunks to 200 chars
60+
- **Unified Toggle**`Ctrl+Shift+V` (or `⌘+Shift+V` on Mac) toggles both mic and TTS together
61+
- **Smart Voice Input Targeting** — voice text goes to: last focused Monaco editor → focused input/textarea → fallback to ad-hoc chat (auto-opens panel)
62+
- **Robust Restart** — Chrome kills recognition after ~60s silence; auto-restarts with 300ms backoff
63+
- **Iframe Support** — requires `allow="microphone"` on the iframe tag
64+
65+
### Editor & Layout
66+
- **Monaco Editors** — syntax-highlighted editors for all steps, code, and tests
67+
- **Language Selector** — Python, Java, JavaScript (Python/Java disabled when server unavailable)
68+
- **Maximize/Restore (⤢)** — any editor can go fullscreen with `Ctrl+Shift+M` or click
69+
- **Docked Terminal** — when code editor is maximized, terminal docks at bottom with its own run/test buttons
70+
- **Description Dock** — toggle description between top bar and left panel (`⬒ Dock Left` / `⬓ Dock Top`)
71+
- **Description Auto-Hide** — collapse description, peek on hover
72+
- **Collapsible Sidebar** — hamburger toggle, auto-expand on hover when collapsed
73+
- **Terminal** — color-coded output with spinner animations during async operations
74+
75+
### Persistence
76+
- **localStorage First** — all state always written to localStorage
77+
- **Server Mirror** — when Flask server is available, state is mirrored to disk (`workspace_state.json`)
78+
- **Conflict Resolution** — localStorage wins on load; falls back to server if localStorage is empty
79+
- **Clobber Prevention**`stateLoaded` flag prevents saving before load completes; empty-tabs guard prevents overwriting good data
80+
- **API Key** — stored only in localStorage, never sent to server for storage
81+
- **Terminal State** — per-tab terminal output and color persisted across tab switches
82+
83+
### Status Bar
84+
- Thin 24px bar at bottom of viewport
85+
- **Left**: voice state with breathing pulse (`🎤 Listening · 🔊 Speaking` or `🎤 Voice off · Ctrl+Shift+V`)
86+
- **Right**: sync/server status (`Browser-Only Mode`, `Syncing...`, `Saved`, `Server Connected`)
87+
88+
---
89+
90+
## Keyboard Shortcuts
91+
92+
| Shortcut | Action |
93+
|---|---|
94+
| `Ctrl+S` | Save |
95+
| `Ctrl+'` | Run Logic |
96+
| `Ctrl+"` | Generate Tests |
97+
| `Ctrl+Shift+L` | Clear Terminal |
98+
| `Ctrl+Shift+M` | Maximize/Restore focused editor |
99+
| `Ctrl+Shift+V` | Toggle Voice (STT + TTS) |
100+
| `Escape` | Cancel test gen timer / stop mic / stop TTS / restore maximized editor |
101+
102+
---
103+
104+
## Running
105+
106+
### Browser-Only (No Server)
107+
108+
Open `templates/index.html` directly or serve with any static file server. JavaScript execution works out of the box. Set your Gemini API key via the `{🗝}` button.
109+
110+
### With Flask Server
111+
112+
```bash
113+
pip install flask
114+
python server.py
115+
```
116+
117+
Server runs on port **8500**. Enables:
118+
- Python and Java code execution
119+
- LeetCode problem fetching
120+
- Server-side sanity checking
121+
- Workspace state mirroring to disk
122+
123+
### Embedding as iframe
124+
125+
```html
126+
<iframe src="https://your-domain/..." allow="microphone" style="width:100%;height:100vh;border:none;"></iframe>
127+
```
128+
129+
The `allow="microphone"` attribute is required for voice features.
130+
131+
---
132+
133+
## Project Structure
134+
135+
```
136+
├── templates/
137+
│ └── index.html # Single-page app (~1600 lines)
138+
├── server.py # Optional Flask backend (port 8500)
139+
├── leetcode_cache.json # Cached problem list (367KB)
140+
└── workspace_state.json # Server-side state mirror
141+
```
142+
143+
---
144+
145+
## Tech Stack
146+
147+
- **Frontend**: Vanilla JS, Monaco Editor, Tailwind CSS (CDN), Marked.js
148+
- **AI**: Google Gemini API (direct or proxied)
149+
- **Voice**: Web Speech API (SpeechRecognition + SpeechSynthesis)
150+
- **Backend** (optional): Flask, Python subprocess for code execution

templates/index.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,8 +879,13 @@ <h3 class="text-xl font-black text-white mb-6 tracking-tighter italic">OPEN NEW
879879
};
880880
window.addEventListener('message', handler);
881881
const wrapped = `<script>
882-
console.log = (...a) => parent.postMessage(a.map(String).join(' '), '*');
883-
console.error = console.log;
882+
const _log = console.log.bind(console), _err = console.error.bind(console), _warn = console.warn.bind(console), _info = console.info.bind(console), _table = console.table.bind(console);
883+
const _send = (...a) => parent.postMessage(a.map(v => typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v)).join(' '), '*');
884+
console.log = (...a) => { _log(...a); _send(...a); };
885+
console.error = (...a) => { _err(...a); _send(...a); };
886+
console.warn = (...a) => { _warn(...a); _send(...a); };
887+
console.info = (...a) => { _info(...a); _send(...a); };
888+
console.table = (data, cols) => { _table(data, cols); _send(typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data)); };
884889
try { ${code}\n } catch(e) { console.log('ERROR: ' + e.message); }
885890
parent.postMessage('__DONE__', '*');
886891
<\/script>`;

0 commit comments

Comments
 (0)